diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a13ad60c6..f1f9371d2 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: [leaanthony] +github: [ leaanthony ] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username diff --git a/.github/workflows/deploy-website-to-wails.top-mirror.yml b/.github/workflows/deploy-website-to-wails.top-mirror.yml new file mode 100644 index 000000000..88966f189 --- /dev/null +++ b/.github/workflows/deploy-website-to-wails.top-mirror.yml @@ -0,0 +1,35 @@ +name: Deploy mirror | 部署镜像 + +on: + push: + branches: [master] + # pull_request: + # branches: [ main ] + +jobs: + build-and-deploy: + name: Automatic deployment | 自动部署 + runs-on: ubuntu-latest + if: github.repository == 'misitebao/wails' + + steps: + - name: Checkout | 切换到部署分支 + uses: actions/checkout@v2 + with: + ref: "master" + submodules: true + fetch-depth: 0 + + - name: Build Site | 构建网站 + run: | + cd website && + npm install && npm run build + + - name: Deploy to Server | 部署到服务器 + uses: hengkx/ssh-deploy@v1.0.1 + with: + HOST: ${{ secrets.DEPLOY_HOST }} + USERNAME: ${{ secrets.DEPLOY_HOST_USER }} + PASSWORD: ${{ secrets.DEPLOY_HOST_PASSWORD }} + SOURCE: "website/build" + TARGET: "/www/wwwroot/wails.top" diff --git a/.github/workflows/latest-pre.yml b/.github/workflows/latest-pre.yml index d7c865a37..870fe55d5 100644 --- a/.github/workflows/latest-pre.yml +++ b/.github/workflows/latest-pre.yml @@ -1,8 +1,10 @@ name: latest pre-release on: push: + branches: + - develop tags: - - '**-pre**' + - '**-pre**' jobs: build: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4ecf5c57e..df5e53697 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -29,4 +29,4 @@ jobs: run: go build -v ./cmd/wails - name: Test - run: ./wails version \ No newline at end of file + run: ./wails version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c22c6bb48..03fe71f98 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,4 +31,4 @@ jobs: run: go build -v ./cmd/wails - name: Test - run: ./wails version \ No newline at end of file + run: ./wails version diff --git a/.github/workflows/runtime.yml b/.github/workflows/runtime.yml new file mode 100644 index 000000000..43cc5bdff --- /dev/null +++ b/.github/workflows/runtime.yml @@ -0,0 +1,29 @@ +name: Runtime +on: + push: + branches: + - v2-alpha + paths: + - 'v2/internal/frontend/runtime/**' +jobs: + rebuild-runtime: + name: Rebuild the runtime + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 14.17.6 + cache: 'npm' + cache-dependency-path: v2/internal/frontend/runtime/package-lock.json + - run: npm install + working-directory: v2/internal/frontend/runtime + - run: npm run build + working-directory: v2/internal/frontend/runtime + + - name: Commit changes + uses: devops-infra/action-commit-push@master + with: + github_token: "${{ secrets.GITHUB_TOKEN }}" + commit_prefix: "[AUTO]" + commit_message: "The runtime was rebuilt" diff --git a/.gitignore b/.gitignore index e2476fdc9..620386a67 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,17 @@ cmd/wails/wails .DS_Store tmp node_modules/ +package.json.md5 +v2/test/**/frontend/dist +v2/test/**/build/ +v2/test/frameless/icon.png +v2/test/hidden/icon.png +v2/test/kitchensink/frontend/public/bundle.* +v2/pkg/parser/testproject/frontend/wails v2/test/kitchensink/frontend/public -v2/internal/ffenestri/runtime.c -v2/internal/runtime/assets/desktop.js v2/test/kitchensink/build/darwin/desktop/kitchensink v2/test/kitchensink/frontend/package.json.md5 +/v2/internal/ffenestri/windows/test/cmake-build-debug/ +!v2/internal/ffenestri/windows/x64/webview2.dll +!v2/internal/ffenestri/windows/x64/WebView2Loader.dll +.idea/ diff --git a/.vscode/settings.json b/.vscode/settings.json index dde13a901..da2d84ba6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,8 @@ { "go.formatTool": "goimports", - "eslint.alwaysShowStatus": true + "eslint.alwaysShowStatus": true, + "files.associations": { + "__locale": "c", + "ios": "c" + } } \ No newline at end of file diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d5119e928..72d6bd9d9 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -31,18 +31,18 @@ Wails is what it is because of the time and effort given by these great people. * [Christopher Murphy](https://github.com/Splode) * [Zámbó, Levente](https://github.com/Lyimmi) * [artem](https://github.com/Unix4ever) - * [Tim Kipp](https://github.com/timkippdev) - * [Dmitry Gomzyakov](https://github.com/kyoto44) - * [Arthur Wiebe](https://github.com/artooro) - * [Ilgıt Yıldırım](https://github.com/ilgityildirim) - * [Altynbek](https://github.com/gelleson) - * [Kyle](https://github.com/kmuchmore) - * [Balakrishna Prasad Ganne](https://github.com/aayush420) - * [Charaf Rezrazi](https://github.com/Rezrazi) - * [misitebao](https://github.com/misitebao) - * [Elie Grenon](https://github.com/DrunkenPoney) - * [SophieAu](https://github.com/SophieAu) - * [Alexander Matviychuk](https://github.com/alexmat) - * [RH12503](https://github.com/RH12503) - * [hi019](https://github.com/hi019) - * [Igor Minen](https://github.com/Igogrek) +* [Tim Kipp](https://github.com/timkippdev) +* [Dmitry Gomzyakov](https://github.com/kyoto44) +* [Arthur Wiebe](https://github.com/artooro) +* [Ilgıt Yıldırım](https://github.com/ilgityildirim) +* [Altynbek](https://github.com/gelleson) +* [Kyle](https://github.com/kmuchmore) +* [Balakrishna Prasad Ganne](https://github.com/aayush420) +* [Charaf Rezrazi](https://github.com/Rezrazi) +* [misitebao](https://github.com/misitebao) +* [Elie Grenon](https://github.com/DrunkenPoney) +* [SophieAu](https://github.com/SophieAu) +* [Alexander Matviychuk](https://github.com/alexmat) +* [RH12503](https://github.com/RH12503) +* [hi019](https://github.com/hi019) +* [Igor Minen](https://github.com/Igogrek) diff --git a/README.md b/README.md index 782d57e1f..545fd231a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

-
+

Build desktop applications using Go & Web Technologies.

@@ -18,12 +18,16 @@ ## Internationalization -English | [简体中文](README.zh_CN.md) +English | [简体中文](README.zh-Hans.md) -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! +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! The official docs can be found at [https://wails.app](https://wails.app). +Click [here](https://wails.io) if you are interested in trying out v2 Beta for Windows. + ## Contents @@ -33,22 +37,21 @@ The official docs can be found at [https://wails.app](https://wails.app). - [3. Features](#nav-3) - [4. Sponsors](#nav-4) - [5. Installation](#nav-5) - - [5.1 MacOS](#nav-5-1) - - [5.2 Linux](#nav-5-2) - - [5.2.1 Debian/Ubuntu](#nav-5-2-1) - - [5.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-5-2-2) - - [5.2.3 Centos](#nav-5-2-3) - - [5.2.4 Fedora](#nav-5-2-4) - - [5.2.5 VoidLinux & VoidLinux-musl](#nav-5-2-5) - - [5.2.6 Gentoo](#nav-5-2-6) - - [5.3 Windows](#nav-5-3) + - [5.1 MacOS](#nav-5-1) + - [5.2 Linux](#nav-5-2) + - [5.2.1 Debian/Ubuntu](#nav-5-2-1) + - [5.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-5-2-2) + - [5.2.3 Centos](#nav-5-2-3) + - [5.2.4 Fedora](#nav-5-2-4) + - [5.2.5 VoidLinux & VoidLinux-musl](#nav-5-2-5) + - [5.2.6 Gentoo](#nav-5-2-6) + - [5.3 Windows](#nav-5-3) - [6. Installation](#nav-6) - [7. Next Steps](#nav-7) - [8. FAQ](#nav-8) - [9. Contributors](#nav-9) - [10. Special Mentions](#nav-10) -- [11. Licensing](#nav-11) -- [12. Special Thanks](#nav-12) +- [11. Special Thanks](#nav-11) @@ -70,33 +73,81 @@ The official docs can be found at [https://wails.app](https://wails.app). This project is supported by these kind people / companies: - - - - + + - - + + - - + + - - +
+
+
+ - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## 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: +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.16 - npm @@ -165,7 +216,9 @@ _Fedora 29, 30_ ### 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. +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. @@ -191,15 +244,20 @@ It is recommended at this stage to read the comprehensive documentation at [http - 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. + 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 HTML/JS/CSS frontend with their applications, without resorting to creating a server and opening a browser to view it. + Go programmers who want to bundle an HTML/JS/CSS frontend with their applications, without resorting to creating a + server and opening a browser to view it. - What's with the name? - When I saw WebView, I thought "What I really want is tooling around building a WebView app, a bit like Rails is to Ruby". So initially it was a play on words (Webview on Rails). It just so happened to also be a homophone of the English name for the [Country](https://en.wikipedia.org/wiki/Wales) I am from. So it stuck. + When I saw WebView, I thought "What I really want is tooling around building a WebView app, a bit like Rails is to + Ruby". So initially it was a play on words (Webview on Rails). It just so happened to also be a homophone of the + English name for the [Country](https://en.wikipedia.org/wiki/Wales) I am from. So it stuck. @@ -250,6 +308,7 @@ It is recommended at this stage to read the comprehensive documentation at [http + @@ -257,9 +316,12 @@ It is recommended at this stage to read the comprehensive documentation at [http 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 (Not long now Dustin!). -- [Serge Zaitsev](https://github.com/zserge) - Creator of [Webview](https://github.com/zserge/webview) which Wails uses for the windowing. -- [Byron](https://github.com/bh90210) - At times, Byron has single handedly kept this project alive. Without his incredible input, we never would have got to v1. +- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and feedback has been immense. More patience than + you can throw a stick at (Not long now Dustin!). +- [Serge Zaitsev](https://github.com/zserge) - Creator of [Webview](https://github.com/zserge/webview) which Wails uses + for the windowing. +- [Byron](https://github.com/bh90210) - At times, Byron has single handedly kept this project alive. Without his + incredible input, we never would have got to v1. This project was mainly coded to the following albums: @@ -279,17 +341,11 @@ This project was mainly coded to the following albums: -## Licensing - -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large) - - - ## Special Thanks


- A *huge* thanks to Pace for sponsoring the project and helping the efforts to get Wails ported to Apple Silicon!

+ A huge thanks to Pace for sponsoring the project and helping the efforts to get Wails ported to Apple Silicon!

If you are looking for a Project Management tool that's powerful but quick and easy to use, check them out!

diff --git a/README.zh_CN.md b/README.zh-Hans.md similarity index 68% rename from README.zh_CN.md rename to README.zh-Hans.md index 547adc16d..fc310183e 100644 --- a/README.zh_CN.md +++ b/README.zh-Hans.md @@ -1,5 +1,5 @@

-
+

使用 Go 和 Web 技术构建桌面应用程序。

@@ -20,10 +20,12 @@ [English](README.md) | 简体中文 -向 Go 程序提供 Web 接口的传统方法是通过内置 Web 服务器。Wails 提供了一种不同的方法:它提供了将 Go 代码和 Web 前端都包装成单个二进制文件的能力。通过处理项目创建、编译和打包,可为您提供工具,使您轻松做到这一点。你所要做的就是发挥创造力! +向 Go 程序提供 Web 接口的传统方法是通过内置 Web 服务器。Wails 提供了一种不同的方法:它提供了将 Go 代码和 Web +前端都包装成单个二进制文件的能力。通过提供工具,可以很轻松的完成项目的创建、编译和打包。你所要做的就是发挥创意! -官方文档可以在 [https://wails.app](https://wails.app)中找到。 -国内镜像站点 [https://wails.top](https://wails.top) +官方文档可以在 [https://wails.app](https://wails.app) 中找到。 + +国内镜像站点 [https://wails.top](https://wails.top)。 @@ -34,15 +36,15 @@ - [3. 特征](#nav-3) - [4. 赞助商](#nav-4) - [5. 安装](#nav-5) - - [5.1 MacOS](#nav-5-1) - - [5.2 Linux](#nav-5-2) - - [5.2.1 Debian/Ubuntu](#nav-5-2-1) - - [5.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-5-2-2) - - [5.2.3 Centos](#nav-5-2-3) - - [5.2.4 Fedora](#nav-5-2-4) - - [5.2.5 VoidLinux & VoidLinux-musl](#nav-5-2-5) - - [5.2.6 Gentoo](#nav-5-2-6) - - [5.3 Windows](#nav-5-3) + - [5.1 MacOS](#nav-5-1) + - [5.2 Linux](#nav-5-2) + - [5.2.1 Debian/Ubuntu](#nav-5-2-1) + - [5.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-5-2-2) + - [5.2.3 Centos](#nav-5-2-3) + - [5.2.4 Fedora](#nav-5-2-4) + - [5.2.5 VoidLinux & VoidLinux-musl](#nav-5-2-5) + - [5.2.6 Gentoo](#nav-5-2-6) + - [5.3 Windows](#nav-5-3) - [6. 安装](#nav-6) - [7. 下一步](#nav-7) - [8. 常见问题](#nav-8) @@ -71,29 +73,81 @@ 这个项目由以下这些人或者公司支持: - - - - + + + - - + + - - + + - - +
+
+
+ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## 安装 -Wails 使用 cgo 与原生渲染引擎结合,因此需要一些依赖平台的库以及 Go 的安装。基本要求是: +Wails 使用 cgo 与原生渲染引擎结合,因此需要依赖一些平台的库以及 Go 的安装。基本要求是: - Go 1.16 - npm @@ -120,7 +174,7 @@ _Debian: 8, 9, 10_ _Ubuntu: 16.04, 18.04, 19.04_ -_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!\_OS +_也成功测试了: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!\_OS @@ -128,7 +182,7 @@ _Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, K `sudo pacman -S webkit2gtk gtk3` -_Also succesfully test on: Manjaro & ArcoLinux_ +_也成功测试了: Manjaro & ArcoLinux_ @@ -162,13 +216,13 @@ _Fedora 29, 30_ ### Windows -Windows 需要 GCC 和相关工具。 建议从 [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download)下载, 安装完成,您就可以开始了。 +Windows 需要 GCC 和相关工具。 建议从 [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download) 下载, 安装完成,您就可以开始了。 ## 安装 -**确保 Go modules 是开启的: GO111MODULE=on 并且 go/bin 在您的 PATH 变量中.** +**确保 Go modules 是开启的:GO111MODULE=on 并且 go/bin 在您的 PATH 变量中。** 安装很简单,运行以下命令: @@ -180,7 +234,7 @@ go get -u github.com/wailsapp/wails/cmd/wails ## 下一步 -建议在此时阅读[https://wails.app](https://wails.app)上面的文档. +建议在此时阅读 [https://wails.app](https://wails.app) 上面的文档. @@ -188,15 +242,16 @@ go get -u github.com/wailsapp/wails/cmd/wails - 它是 Electron 的替代品吗? - 取决于您的要求。它旨在使 Go 程序员可以轻松制作轻量级桌面应用程序或在其现有应用程序中添加前端。尽管 Wails 当前不提供对诸如菜单之类的本机元素的钩子,但将来可能会改变。 + 取决于您的要求。它旨在使 Go 程序员可以轻松制作轻量级桌面应用程序或在其现有应用程序中添加前端。尽管 Wails 当前不提供对诸如菜单之类的原生元素的钩子,但将来可能会改变。 - 这个项目针对的是谁? - 希望将 HTML / JS / CSS 前端与其应用程序捆绑在一起的程序员,而无需借助创建服务并打开浏览器进行查看的方式。 + 希望将 HTML / JS / CSS 前端与其应用程序捆绑在一起的程序员,而不是借助创建服务并打开浏览器进行查看的方式。 - 名字怎么来的? - 当我看到 WebView 时,我想"我真正想要的是围绕构建 WebView 应用程序工作,有点像 Rails 对于 Ruby"。因此,最初它是一个文字游戏(Webview on Rails)。碰巧也是我来自的[国家](https://en.wikipedia.org/wiki/Wales)的英文名字的同音。所以就是他了。 + 当我看到 WebView 时,我想"我真正想要的是围绕构建 WebView 应用程序工作,有点像 Rails 对于 Ruby"。因此,最初它是一个文字游戏(Webview on + Rails)。碰巧也是我来自的 [国家](https://en.wikipedia.org/wiki/Wales) 的英文名字的同音。所以就是他了。 @@ -252,11 +307,11 @@ go get -u github.com/wailsapp/wails/cmd/wails ## 特别提及 -如果没有以下人员,此项目将永远不会存在: +如果没有以下人员,此项目或许永远不会存在: -- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and feedback has been immense. More patience than you can throw a stick at (Not long now Dustin!). -- [Serge Zaitsev](https://github.com/zserge) - Creator of [Webview](https://github.com/zserge/webview) which Wails uses for the windowing. -- [Byron](https://github.com/bh90210) - At times, Byron has single handedly kept this project alive. Without his incredible input, we never would have got to v1. +- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - 他的支持和反馈是巨大的。 +- [Serge Zaitsev](https://github.com/zserge) - Wails 窗口所使用的 [Webview](https://github.com/zserge/webview) 的作者。 +- [Byron](https://github.com/bh90210) - 有时,Byron 单枪匹马地保持这个项目活着。没有他令人难以置信的投入,我们永远不会得到 v1 。 This project was mainly coded to the following albums: @@ -286,12 +341,12 @@ This project was mainly coded to the following albums:


- 非常感谢Pace对项目的赞助,并帮助将Wails移植到Apple Silicon

- 如果您正在寻找一个强大的项目管理工具,并且快速和易于使用,可以看看他们!

+ 非常感谢Pace对项目的赞助,并帮助将 Wails 移植到 Apple Silicon !

+ 如果您正在寻找一个强大并且快速和易于使用的项目管理工具,可以看看他们!

- 特别感谢JetBrains向我们捐赠许可!

- 请点击logo让他们知道你的感激之情!

+ 特别感谢 JetBrains 向我们捐赠许可!

+ 请点击 logo 让他们知道你的感激之情!

diff --git a/app_other.go b/app_other.go index 5d63c4c6c..9e0514350 100644 --- a/app_other.go +++ b/app_other.go @@ -1,3 +1,4 @@ +//go:build linux || darwin || !windows // +build linux darwin !windows package wails diff --git a/app_windows.go b/app_windows.go index 6386d4131..cf9472731 100644 --- a/app_windows.go +++ b/app_windows.go @@ -1,3 +1,4 @@ +//go:build windows || !linux || !darwin // +build windows !linux !darwin package wails diff --git a/cmd/linux.go b/cmd/linux.go index 8ef7836ac..902a75b46 100644 --- a/cmd/linux.go +++ b/cmd/linux.go @@ -73,6 +73,9 @@ const ( RHEL // NixOS distribution NixOS + // Artix linux distribution + ArtixLinux + ) // DistroInfo contains all the information relating to a linux distribution @@ -187,6 +190,8 @@ func parseOsRelease(osRelease string) *DistroInfo { result.Distribution = Crux case "nixos": result.Distribution = NixOS + case "artix": + result.Distribution = ArtixLinux default: result.Distribution = Unknown } diff --git a/cmd/linuxdb.yaml b/cmd/linuxdb.yaml index 52f6a57cd..64628a5ea 100644 --- a/cmd/linuxdb.yaml +++ b/cmd/linuxdb.yaml @@ -213,6 +213,15 @@ distributions: gccversioncommand: *gccdumpversion programs: *archdefaultprograms libraries: *archdefaultlibraries + artix: + id: artix + releases: + default: + version: default + name: Artix Linux + gccversioncommand: *gccdumpversion + programs: *archdefaultprograms + libraries: *archdefaultlibraries ctlos: id: ctlos releases: @@ -364,4 +373,3 @@ distributions: help: Please install with `nix-env -iA nixos.gtk3` - name: webkitgtk help: Please install with `nix-env -iA nixos.nodePackages.webkitgtk` - diff --git a/cmd/system.go b/cmd/system.go index 5c2aa6f3c..49e4f760f 100644 --- a/cmd/system.go +++ b/cmd/system.go @@ -281,7 +281,7 @@ func CheckDependencies(logger *Logger) (bool, error) { switch distroInfo.Distribution { case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS: libraryChecker = DpkgInstalled - case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS: + case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS, ArtixLinux: libraryChecker = PacmanInstalled case CentOS, Fedora, Tumbleweed, Leap, RHEL: libraryChecker = RpmInstalled diff --git a/cmd/templates/svelte/frontend/rollup.config.js b/cmd/templates/svelte/frontend/rollup.config.js index ac3a0a769..6d0a0a12d 100644 --- a/cmd/templates/svelte/frontend/rollup.config.js +++ b/cmd/templates/svelte/frontend/rollup.config.js @@ -2,7 +2,7 @@ import svelte from 'rollup-plugin-svelte'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import livereload from 'rollup-plugin-livereload'; -import { terser } from 'rollup-plugin-terser'; +import {terser} from 'rollup-plugin-terser'; import image from '@rollup/plugin-image'; import babel from 'rollup-plugin-babel'; import polyfill from 'rollup-plugin-polyfill'; diff --git a/cmd/templates/vue3-js/frontend/README.md b/cmd/templates/vue3-js/frontend/README.md index 8d5cc5314..f56df6be7 100644 --- a/cmd/templates/vue3-js/frontend/README.md +++ b/cmd/templates/vue3-js/frontend/README.md @@ -1,24 +1,29 @@ # vue-js ## Project setup + ``` npm install ``` ### Compiles and hot-reloads for development + ``` npm run serve ``` ### Compiles and minifies for production + ``` npm run build ``` ### Lints and fixes files + ``` npm run lint ``` ### Customize configuration + See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/cmd/templates/vue3-js/frontend/babel.config.js b/cmd/templates/vue3-js/frontend/babel.config.js index e9558405f..c94e72931 100644 --- a/cmd/templates/vue3-js/frontend/babel.config.js +++ b/cmd/templates/vue3-js/frontend/babel.config.js @@ -1,5 +1,5 @@ module.exports = { - presets: [ - '@vue/cli-plugin-babel/preset' - ] + presets: [ + '@vue/cli-plugin-babel/preset' + ] } diff --git a/cmd/templates/vue3-js/frontend/public/index.html b/cmd/templates/vue3-js/frontend/public/index.html index 3e5a13962..6ba8b205d 100644 --- a/cmd/templates/vue3-js/frontend/public/index.html +++ b/cmd/templates/vue3-js/frontend/public/index.html @@ -1,17 +1,18 @@ - + - - - + + + <%= htmlWebpackPlugin.options.title %> - - - -
- - + + + +
+ + diff --git a/cmd/templates/vue3-js/frontend/src/main.js b/cmd/templates/vue3-js/frontend/src/main.js index c65e18a02..fb6c9ce07 100644 --- a/cmd/templates/vue3-js/frontend/src/main.js +++ b/cmd/templates/vue3-js/frontend/src/main.js @@ -1,4 +1,4 @@ -import { createApp } from 'vue' +import {createApp} from 'vue' import App from './App.vue' import router from './router' import store from './store' @@ -8,7 +8,7 @@ import * as Wails from '@wailsapp/runtime'; Wails.Init(() => { createApp(App) - .use(store) - .use(router) - .mount('#app') + .use(store) + .use(router) + .mount('#app') }) diff --git a/cmd/templates/vue3-js/frontend/src/router/index.js b/cmd/templates/vue3-js/frontend/src/router/index.js index 38211ad79..41f7ce976 100644 --- a/cmd/templates/vue3-js/frontend/src/router/index.js +++ b/cmd/templates/vue3-js/frontend/src/router/index.js @@ -1,4 +1,4 @@ -import { createRouter, createMemoryHistory } from 'vue-router' +import {createMemoryHistory, createRouter} from 'vue-router' import Home from '../views/Home.vue' import About from '../views/About.vue' diff --git a/cmd/templates/vue3-js/frontend/src/store/index.js b/cmd/templates/vue3-js/frontend/src/store/index.js index 5f05f1939..a747c1ce9 100644 --- a/cmd/templates/vue3-js/frontend/src/store/index.js +++ b/cmd/templates/vue3-js/frontend/src/store/index.js @@ -1,12 +1,8 @@ -import { createStore } from 'vuex' +import {createStore} from 'vuex' export default createStore({ - state: { - }, - mutations: { - }, - actions: { - }, - modules: { - } + state: {}, + mutations: {}, + actions: {}, + modules: {} }) diff --git a/cmd/templates/vue3-js/frontend/vue.config.js b/cmd/templates/vue3-js/frontend/vue.config.js index 8dcf3e339..72e4fd1d5 100644 --- a/cmd/templates/vue3-js/frontend/vue.config.js +++ b/cmd/templates/vue3-js/frontend/vue.config.js @@ -1,42 +1,42 @@ let cssConfig = {}; if (process.env.NODE_ENV == 'production') { - cssConfig = { - extract: { - filename: '[name].css', - chunkFilename: '[name].css' - } - }; + cssConfig = { + extract: { + filename: '[name].css', + chunkFilename: '[name].css' + } + }; } module.exports = { - chainWebpack: config => { - let limit = 9999999999999999; - config.module - .rule('images') - .test(/\.(png|gif|jpg)(\?.*)?$/i) - .use('url-loader') - .loader('url-loader') - .tap(options => Object.assign(options, { limit: limit })); - config.module - .rule('fonts') - .test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i) - .use('url-loader') - .loader('url-loader') - .options({ - limit: limit - }); - }, - css: cssConfig, - configureWebpack: { - output: { - filename: '[name].js' - }, - optimization: { - splitChunks: false - } - }, - devServer: { - disableHostCheck: true - } + chainWebpack: config => { + let limit = 9999999999999999; + config.module + .rule('images') + .test(/\.(png|gif|jpg)(\?.*)?$/i) + .use('url-loader') + .loader('url-loader') + .tap(options => Object.assign(options, {limit: limit})); + config.module + .rule('fonts') + .test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i) + .use('url-loader') + .loader('url-loader') + .options({ + limit: limit + }); + }, + css: cssConfig, + configureWebpack: { + output: { + filename: '[name].js' + }, + optimization: { + splitChunks: false + } + }, + devServer: { + disableHostCheck: true + } }; diff --git a/cmd/templates/vue3-js/template.json b/cmd/templates/vue3-js/template.json index 3d70af3bc..8cde55215 100644 --- a/cmd/templates/vue3-js/template.json +++ b/cmd/templates/vue3-js/template.json @@ -11,5 +11,8 @@ "serve": "npm run serve", "bridge": "src", "wailsdir": "", - "platforms": ["linux", "darwin"] + "platforms": [ + "linux", + "darwin" + ] } diff --git a/cmd/wails/10.1_dev_newtemplate.go b/cmd/wails/10.1_dev_newtemplate.go index 3c52b45a9..17e4a4c1e 100644 --- a/cmd/wails/10.1_dev_newtemplate.go +++ b/cmd/wails/10.1_dev_newtemplate.go @@ -1,3 +1,4 @@ +//go:build dev // +build dev package main diff --git a/cmd/wails/10_dev.go b/cmd/wails/10_dev.go index 3d237cb98..ea0af0976 100644 --- a/cmd/wails/10_dev.go +++ b/cmd/wails/10_dev.go @@ -1,3 +1,4 @@ +//go:build dev // +build dev package main diff --git a/cmd/windows.go b/cmd/windows.go index 774730f74..1a85f13c1 100644 --- a/cmd/windows.go +++ b/cmd/windows.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package cmd diff --git a/config.go b/config.go index 3833dfdae..0fc80c255 100644 --- a/config.go +++ b/config.go @@ -181,7 +181,7 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) { MaxWidth: -1, MaxHeight: -1, Title: "My Wails App", - Colour: "", + Colour: "", HTML: defaultHTML, } diff --git a/go.mod b/go.mod index ff37b58d6..4d289fe39 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/abadojack/whatlanggo v1.0.1 github.com/fatih/color v1.7.0 github.com/go-playground/colors v1.2.0 - github.com/gorilla/websocket v1.4.0 + github.com/gorilla/websocket v1.4.1 github.com/jackmordaunt/icns v1.0.0 github.com/kennygrant/sanitize v1.2.4 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect @@ -16,12 +16,13 @@ require ( github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 github.com/pkg/errors v0.8.1 // indirect - github.com/sirupsen/logrus v1.4.1 + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/objx v0.1.1 // indirect github.com/stretchr/testify v1.3.0 // indirect github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba golang.org/x/image v0.0.0-20200430140353-33d19683fad8 golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect - golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd + golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 golang.org/x/text v0.3.0 gopkg.in/AlecAivazis/survey.v1 v1.8.4 gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 diff --git a/go.sum b/go.sum index 2c697c813..79a777e39 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/go-playground/colors v1.2.0 h1:0EdjTXKrr2g1L/LQTYtIqabeHpZuGZz1U4osS1T8+5M= github.com/go-playground/colors v1.2.0/go.mod h1:miw1R2JIE19cclPxsXqNdzLZsk4DP4iF+m88bRc7kfM= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ= @@ -54,6 +54,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -76,8 +78,12 @@ golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c= +golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc= diff --git a/lib/renderer/webview.go b/lib/renderer/webview.go index d122a3e2b..777a8560c 100644 --- a/lib/renderer/webview.go +++ b/lib/renderer/webview.go @@ -24,12 +24,12 @@ import ( var UseFirebug = "" type WebView struct { - window wv.WebView // The webview object - ipc interfaces.IPCManager - log *logger.CustomLogger - config interfaces.AppConfig - eventManager interfaces.EventManager - bindingCache []string + window wv.WebView // The webview object + ipc interfaces.IPCManager + log *logger.CustomLogger + config interfaces.AppConfig + eventManager interfaces.EventManager + bindingCache []string maximumSizeSet bool } @@ -111,7 +111,7 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana } // SignalManager.OnExit(w.Exit) - + // Set colour color := config.GetColour() if color != "" { diff --git a/logo.png b/logo.png new file mode 100644 index 000000000..8cfad9043 Binary files /dev/null and b/logo.png differ diff --git a/runtime/js/.eslintrc b/runtime/js/.eslintrc index 85326fe27..426ccc63d 100644 --- a/runtime/js/.eslintrc +++ b/runtime/js/.eslintrc @@ -3,12 +3,12 @@ "browser": true, "es6": true, "amd": true, - "node": true, + "node": true }, "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 2016, - "sourceType": "module", + "sourceType": "module" }, "rules": { "indent": [ diff --git a/runtime/js/core/browser.js b/runtime/js/core/browser.js index ccbe9f284..dd7856897 100644 --- a/runtime/js/core/browser.js +++ b/runtime/js/core/browser.js @@ -26,7 +26,7 @@ export function OpenURL(url) { * Opens the given filename using the system's default file handler * * @export - * @param {sting} filename + * @param {string} filename * @returns */ export function OpenFile(filename) { diff --git a/runtime/js/core/calls.js b/runtime/js/core/calls.js index ea3128e8a..9919ff803 100644 --- a/runtime/js/core/calls.js +++ b/runtime/js/core/calls.js @@ -62,7 +62,7 @@ if (window.crypto) { export function Call(bindingName, data, timeout) { // Timeout infinite by default - if (timeout == null || timeout == undefined) { + if (timeout == null) { timeout = 0; } diff --git a/runtime/js/core/ipc.js b/runtime/js/core/ipc.js index 1285255a7..b453d3ac7 100644 --- a/runtime/js/core/ipc.js +++ b/runtime/js/core/ipc.js @@ -45,7 +45,7 @@ function Invoke(message) { * * @export * @param {string} type - * @param {string} payload + * @param {Object} payload * @param {string=} callbackID */ export function SendMessage(type, payload, callbackID) { diff --git a/runtime/js/core/main.js b/runtime/js/core/main.js index 797a8f1c8..51581aadc 100644 --- a/runtime/js/core/main.js +++ b/runtime/js/core/main.js @@ -10,11 +10,11 @@ The lightweight framework for web-like apps /* jshint esversion: 6 */ import * as Log from './log'; import * as Browser from './browser'; -import { On, OnMultiple, Emit, Notify, Heartbeat, Acknowledge } from './events'; -import { NewBinding } from './bindings'; -import { Callback } from './calls'; -import { AddScript, InjectCSS, InjectFirebug } from './utils'; -import { AddIPCListener } from './ipc'; +import {Acknowledge, Emit, Heartbeat, Notify, On, OnMultiple} from './events'; +import {NewBinding} from './bindings'; +import {Callback} from './calls'; +import {AddScript, InjectCSS, InjectFirebug} from './utils'; +import {AddIPCListener} from './ipc'; import * as Store from './store'; // Initialise global if not already @@ -29,7 +29,7 @@ window.backend = {}; // so we have to use an explicit if statement to prevent webpack from optimizing the code. if (window.external == undefined) { window.external = { - invoke: function(x) { + invoke: function (x) { window.webkit.messageHandlers.external.postMessage(x); } }; @@ -75,7 +75,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) { }; // Use firebug? -if( window.usefirebug ) { +if (window.usefirebug) { InjectFirebug(); } diff --git a/runtime/js/runtime/.npmignore b/runtime/js/runtime/.npmignore index d0a1d1fad..945ce43a9 100644 --- a/runtime/js/runtime/.npmignore +++ b/runtime/js/runtime/.npmignore @@ -1 +1 @@ -bridge.js \ No newline at end of file +index.js \ No newline at end of file diff --git a/runtime/js/runtime/package-lock.json b/runtime/js/runtime/package-lock.json index 78aef35b7..1efda3274 100644 --- a/runtime/js/runtime/package-lock.json +++ b/runtime/js/runtime/package-lock.json @@ -260,9 +260,9 @@ } }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { diff --git a/sponsors/bronze sponsor.png b/sponsors/bronze sponsor.png new file mode 100644 index 000000000..0eeb61e0a Binary files /dev/null and b/sponsors/bronze sponsor.png differ diff --git a/v2/NOTES.md b/v2/NOTES.md new file mode 100644 index 000000000..16bac7122 --- /dev/null +++ b/v2/NOTES.md @@ -0,0 +1,40 @@ + +# Packing linux + + * create app, app.desktop, app.png (512x512) + * chmod +x app! + * ./linuxdeploy-x86_64.AppImage --appdir AppDir -i react.png -d react.desktop -e react --output appimage + + +# Wails Doctor + +Tested on: + + * Debian 8 + * Ubuntu 20.04 + * Ubuntu 19.10 + * Solus 4.1 + * Centos 8 + * Gentoo + * OpenSUSE/leap + * Fedora 31 + +### Development + +Add a new package manager processor here: `v2/internal/system/packagemanager/`. IsAvailable should work even if the package is installed. +Add your new package manager to the list of package managers in `v2/internal/system/packagemanager/packagemanager.go`: + +``` +var db = map[string]PackageManager{ + "eopkg": NewEopkg(), + "apt": NewApt(), + "yum": NewYum(), + "pacman": NewPacman(), + "emerge": NewEmerge(), + "zypper": NewZypper(), +} +``` + +## Gentoo + + * Setup docker image using: emerge-webrsync -x -v diff --git a/v2/README.md b/v2/README.md new file mode 100644 index 000000000..20ebb18b7 --- /dev/null +++ b/v2/README.md @@ -0,0 +1,6 @@ +# Wails v2 ALPHA + +This branch contains WORK IN PROGRESS! There are no guarantees. Use at your peril! + +This document will be updated as progress is made. + diff --git a/v2/cmd/wails/internal/commands/build/README.md b/v2/cmd/wails/internal/commands/build/README.md new file mode 100644 index 000000000..321445073 --- /dev/null +++ b/v2/cmd/wails/internal/commands/build/README.md @@ -0,0 +1,48 @@ +# Build + +The build command processes the Wails project and generates an application binary. + +## Usage + +`wails build ` + +### Flags + +| Flag | Details | Default | +| :------------- | :----------- | :------ | +| -clean | Clean the bin directory before building | | +| -compiler path/to/compiler | Use a different go compiler, eg go1.15beta1 | go | +| -ldflags "custom ld flags" | Use given ldflags | | +| -o path/to/binary | Compile to given path/filename | | +| -k | Keep generated assets | | +| -package | Create a platform specific package | | +| -production | Compile in production mode: -ldflags="-w -s" + "-h windows" on Windows | | +| -tags | Build tags to pass to Go compiler (quoted and space separated) | | +| -upx | Compress final binary with UPX (if installed) | | +| -upxflags "custom flags" | Flags to pass to upx | | +| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 | +| -delve | If true, runs delve on the compiled binary | false | + +## The Build Process + +The build process is as follows: + + - The flags are processed, and an Options struct built containing the build context. + - The type of target is determined, and a custom build process is followed for target. + +### Desktop Target + + - The frontend dependencies are installed. The command is read from the project file `wails.json` under the key `frontend:install` and executed in the `frontend` directory. If this is not defined, it is ignored. + - The frontend is then built. This command is read from the project file `wails.json` under the key `frontend:install` and executed in the `frontend` directory. If this is not defined, it is ignored. + - The project directory is checked to see if the `build` directory exists. If not, it is created and default project assets are copied to it. + - An asset bundle is then created by reading the `html` key from `wails.json` and loading the referenced file. This is then parsed, looking for local Javascript and CSS references. Those files are in turn loaded into memory, converted to C data and saved into the asset bundle located at `build/assets.h`, which also includes the original HTML. + - The application icon is then processed: if there is no `build/appicon.png`, a default icon is copied. On Windows, an `app.ico` file is generated from this png. On Mac, `icons.icns` is generated. + - If there are icons in the `build/tray` directory, these are processed, converted to C data and saved as `build/trayicons.h`, ready for the compilation step. + - If there are icons in the `build/dialog` directory, these are processed, converted to C data and saved as `build/userdialogicons.h`, ready for the compilation step. + - If the `-package` flag is given for a Windows target, the Windows assets in the `build/windows` directory are processed: manifest + icons compiled to a `.syso` file (deleted after compilation). + - If we are building a universal binary for Mac, the application is compiled for both `arm64` and `amd64`. The `lipo` tool is then executed to create the universal binary. + - If we are not building a universal binary for Mac, the application is built using `go build`, using build tags to indicate type of application and build mode (debug/production). + - If the `-upx` flag was provided, `upx` is invoked to compress the binary. Custom flags may be provided using the `-upxflags` flag. + - If the `package` flag is given for a non Windows target, the application is bundled for the platform. On Mac, this creates a `.app` with the processed icons, the `Info.plist` in `build/darwin` and the compiled binary. + + diff --git a/v2/cmd/wails/internal/commands/build/build.go b/v2/cmd/wails/internal/commands/build/build.go new file mode 100644 index 000000000..a74ac497b --- /dev/null +++ b/v2/cmd/wails/internal/commands/build/build.go @@ -0,0 +1,230 @@ +package build + +import ( + "fmt" + "io" + "os" + "os/exec" + "runtime" + "strings" + "text/tabwriter" + "time" + + "github.com/wailsapp/wails/v2/internal/system" + + "github.com/leaanthony/clir" + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/pkg/clilogger" + "github.com/wailsapp/wails/v2/pkg/commands/build" +) + +// AddBuildSubcommand adds the `build` command for the Wails application +func AddBuildSubcommand(app *clir.Cli, w io.Writer) { + + outputType := "desktop" + + validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"}) + + command := app.NewSubCommand("build", "Builds the application") + + // Setup noPackage flag + noPackage := false + command.BoolFlag("noPackage", "Skips platform specific packaging", &noPackage) + + compilerCommand := "go" + command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand) + + compress := false + command.BoolFlag("upx", "Compress final binary with UPX (if installed)", &compress) + + compressFlags := "" + command.StringFlag("upxflags", "Flags to pass to upx", &compressFlags) + + // Setup Platform flag + platform := runtime.GOOS + //command.StringFlag("platform", "Platform to target", &platform) + + // Verbosity + verbosity := 1 + command.IntFlag("v", "Verbosity level (0 - silent, 1 - default, 2 - verbose)", &verbosity) + + // ldflags to pass to `go` + ldflags := "" + command.StringFlag("ldflags", "optional ldflags", &ldflags) + + // tags to pass to `go` + tags := "" + command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags) + + outputFilename := "" + command.StringFlag("o", "Output filename", &outputFilename) + + // Clean build directory + cleanBuildDirectory := false + command.BoolFlag("clean", "Clean the build directory before building", &cleanBuildDirectory) + + webview2 := "download" + command.StringFlag("webview2", "WebView2 installer strategy: download,embed,browser,error.", &webview2) + + skipFrontend := false + command.BoolFlag("s", "Skips building the frontend", &skipFrontend) + + forceBuild := false + command.BoolFlag("f", "Force build application", &forceBuild) + + command.Action(func() error { + + quiet := verbosity == 0 + + // Create logger + logger := clilogger.New(w) + logger.Mute(quiet) + + // Validate output type + if !validTargetTypes.Contains(outputType) { + return fmt.Errorf("output type '%s' is not valid", outputType) + } + + if !quiet { + app.PrintBanner() + } + + // Check platform + validPlatformArch := slicer.String([]string{ + "darwin", + "darwin/amd64", + "darwin/arm64", + "darwin/universal", + "linux", + //"linux/amd64", + //"linux/arm-7", + "windows", + "windows/amd64", + }) + if !validPlatformArch.Contains(platform) { + return fmt.Errorf("platform %s is not supported", platform) + } + + if compress && platform == "darwin/universal" { + logger.Println("Warning: compress flag unsupported for universal binaries. Ignoring.") + compress = false + } + + // Lookup compiler path + compilerPath, err := exec.LookPath(compilerCommand) + if err != nil { + return fmt.Errorf("unable to find compiler: %s", compilerCommand) + } + + // Tags + experimental := false + userTags := []string{} + for _, tag := range strings.Split(tags, " ") { + thisTag := strings.TrimSpace(tag) + if thisTag != "" { + userTags = append(userTags, thisTag) + } + if thisTag == "exp" { + experimental = true + } + } + + if runtime.GOOS == "darwin" && !experimental { + return fmt.Errorf("MacOS version coming soon!") + } + + // Webview2 installer strategy (download by default) + wv2rtstrategy := "" + webview2 = strings.ToLower(webview2) + if webview2 != "" { + validWV2Runtime := slicer.String([]string{"download", "embed", "browser", "error"}) + if !validWV2Runtime.Contains(webview2) { + return fmt.Errorf("invalid option for flag 'webview2': %s", webview2) + } + // These are the build tags associated with the strategies + switch webview2 { + case "embed": + wv2rtstrategy = "wv2runtime.embed" + case "error": + wv2rtstrategy = "wv2runtime.error" + case "browser": + wv2rtstrategy = "wv2runtime.browser" + } + } + + // Create BuildOptions + buildOptions := &build.Options{ + Logger: logger, + OutputType: outputType, + OutputFile: outputFilename, + CleanBuildDirectory: cleanBuildDirectory, + Mode: build.Production, + Pack: !noPackage, + LDFlags: ldflags, + Compiler: compilerCommand, + Verbosity: verbosity, + ForceBuild: forceBuild, + IgnoreFrontend: skipFrontend, + Compress: compress, + CompressFlags: compressFlags, + UserTags: userTags, + WebView2Strategy: wv2rtstrategy, + } + + // Calculate platform and arch + platformSplit := strings.Split(platform, "/") + buildOptions.Platform = platformSplit[0] + if system.IsAppleSilicon { + buildOptions.Arch = "arm64" + } else { + buildOptions.Arch = runtime.GOARCH + } + if len(platformSplit) == 2 { + buildOptions.Arch = platformSplit[1] + } + + // Start a new tabwriter + w := new(tabwriter.Writer) + w.Init(os.Stdout, 8, 8, 0, '\t', 0) + + // Write out the system information + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType) + fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform) + fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch) + fmt.Fprintf(w, "Compiler: \t%s\n", compilerPath) + fmt.Fprintf(w, "Skip Frontend: \t%t\n", skipFrontend) + fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress) + fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack) + fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory) + fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags) + fmt.Fprintf(w, "Tags: \t[%s]\n", strings.Join(buildOptions.UserTags, ",")) + if len(buildOptions.OutputFile) > 0 { + fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile) + } + fmt.Fprintf(w, "\n") + w.Flush() + + return doBuild(buildOptions) + }) +} + +// doBuild is our main build command +func doBuild(buildOptions *build.Options) error { + + // Start Time + start := time.Now() + + outputFilename, err := build.Build(buildOptions) + if err != nil { + return err + } + + // Output stats + elapsed := time.Since(start) + buildOptions.Logger.Println("") + buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String())) + buildOptions.Logger.Println("") + + return nil +} diff --git a/v2/cmd/wails/internal/commands/dev/README.md b/v2/cmd/wails/internal/commands/dev/README.md new file mode 100644 index 000000000..96eb3d2a2 --- /dev/null +++ b/v2/cmd/wails/internal/commands/dev/README.md @@ -0,0 +1,22 @@ +# Dev + +The dev command allows you to develop your application through a standard browser. + +## Usage + +`wails dev ` + +### Flags + +| Flag | Details | Default | +| :------------- | :----------- | :------ | +| -compiler path/to/compiler | Use a different go compiler, eg go1.15beta1 | go | +| -ldflags "custom ld flags" | Use given ldflags | | +| -e list,of,extensions | File extensions to trigger rebuilds | go | +| -w | Show warnings | false | +| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 | +| -loglevel | Loglevel to pass to the application - Trace, Debug, Info, Warning, Error | Debug | + +## How it works + +The project is built using a special mode that starts a webserver and starts listening to port 34115. When the frontend project is run independently, so long as the JS is wrapped with the runtime method `ready`, then the frontend will connect to the backend code via websockets. The interface should be present in your browser, and you should be able to interact with the backend as you would in a desktop app. \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/dev/dev.go b/v2/cmd/wails/internal/commands/dev/dev.go new file mode 100644 index 000000000..e9344ea4a --- /dev/null +++ b/v2/cmd/wails/internal/commands/dev/dev.go @@ -0,0 +1,576 @@ +package dev + +import ( + "context" + "fmt" + "io" + "net/http" + "os" + "os/exec" + "os/signal" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "syscall" + "time" + + "github.com/wailsapp/wails/v2/cmd/wails/internal" + "github.com/wailsapp/wails/v2/internal/gomod" + + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/project" + + "github.com/pkg/browser" + "github.com/wailsapp/wails/v2/internal/colour" + + "github.com/fsnotify/fsnotify" + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/internal/process" + "github.com/wailsapp/wails/v2/pkg/clilogger" + "github.com/wailsapp/wails/v2/pkg/commands/build" +) + +const defaultDevServerURL = "http://localhost:34115" + +func LogGreen(message string, args ...interface{}) { + text := fmt.Sprintf(message, args...) + println(colour.Green(text)) +} + +func LogRed(message string, args ...interface{}) { + text := fmt.Sprintf(message, args...) + println(colour.Red(text)) +} + +func LogDarkYellow(message string, args ...interface{}) { + text := fmt.Sprintf(message, args...) + println(colour.DarkYellow(text)) +} + +func sliceToMap(input []string) map[string]struct{} { + result := map[string]struct{}{} + for _, value := range input { + result[value] = struct{}{} + } + return result +} + +type devFlags struct { + ldflags string + compilerCommand string + assetDir string + extensions string + openBrowser bool + noReload bool + wailsjsdir string + tags string + verbosity int + loglevel string + forceBuild bool + debounceMS int + devServerURL string +} + +// AddSubcommand adds the `dev` command for the Wails application +func AddSubcommand(app *clir.Cli, w io.Writer) error { + + command := app.NewSubCommand("dev", "Development mode") + + flags := defaultDevFlags() + command.StringFlag("ldflags", "optional ldflags", &flags.ldflags) + command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &flags.compilerCommand) + command.StringFlag("assetdir", "Serve assets from the given directory", &flags.assetDir) + command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go", &flags.extensions) + command.BoolFlag("browser", "Open application in browser", &flags.openBrowser) + command.BoolFlag("noreload", "Disable reload on asset change", &flags.noReload) + command.StringFlag("wailsjsdir", "Directory to generate the Wails JS modules", &flags.wailsjsdir) + command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &flags.tags) + command.IntFlag("v", "Verbosity level (0 - silent, 1 - standard, 2 - verbose)", &flags.verbosity) + command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &flags.loglevel) + command.BoolFlag("f", "Force build application", &flags.forceBuild) + command.IntFlag("debounce", "The amount of time to wait to trigger a reload on change", &flags.debounceMS) + command.StringFlag("devserverurl", "The url of the dev server to use", &flags.devServerURL) + + command.Action(func() error { + // Create logger + logger := clilogger.New(w) + app.PrintBanner() + + experimental := false + userTags := []string{} + for _, tag := range strings.Split(flags.tags, " ") { + thisTag := strings.TrimSpace(tag) + if thisTag != "" { + userTags = append(userTags, thisTag) + } + if thisTag == "exp" { + experimental = true + } + } + + if runtime.GOOS == "darwin" && !experimental { + return fmt.Errorf("MacOS version coming soon!") + } + + cwd, err := os.Getwd() + if err != nil { + return err + } + + projectConfig, err := loadAndMergeProjectConfig(cwd, &flags) + if err != nil { + return err + } + + // Update go.mod to use current wails version + err = syncGoModVersion(cwd) + if err != nil { + return err + } + + // Run go mod tidy to ensure we're up to date + err = runCommand(cwd, false, "go", "mod", "tidy") + if err != nil { + return err + } + + if flags.tags != "" { + err = runCommand(cwd, true, "wails", "generate", "module", "-tags", flags.tags) + } else { + err = runCommand(cwd, true, "wails", "generate", "module") + } + if err != nil { + return err + } + + // frontend:dev server command + if projectConfig.DevCommand != "" { + var devCommandWaitGroup sync.WaitGroup + closer := runFrontendDevCommand(cwd, projectConfig.DevCommand, &devCommandWaitGroup) + defer closer(&devCommandWaitGroup) + } + + buildOptions := generateBuildOptions(flags) + buildOptions.Logger = logger + buildOptions.UserTags = internal.ParseUserTags(flags.tags) + + var debugBinaryProcess *process.Process = nil + + // Setup signal handler + quitChannel := make(chan os.Signal, 1) + signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM) + exitCodeChannel := make(chan int, 1) + + // Do initial build + logger.Println("Building application for development...") + newProcess, appBinary, err := restartApp(buildOptions, debugBinaryProcess, flags, exitCodeChannel) + if err != nil { + return err + } + if newProcess != nil { + debugBinaryProcess = newProcess + } + + // open browser + if flags.openBrowser { + url := defaultDevServerURL + if flags.devServerURL != "" { + url = flags.devServerURL + } + err = browser.OpenURL(url) + if err != nil { + return err + } + } + + // create the project files watcher + watcher, err := initialiseWatcher(cwd, logger.Fatal) + defer func(watcher *fsnotify.Watcher) { + err := watcher.Close() + if err != nil { + logger.Fatal(err.Error()) + } + }(watcher) + + LogGreen("Watching (sub)/directory: %s", cwd) + LogGreen("Using Dev Server URL: %s", flags.devServerURL) + LogGreen("Using reload debounce setting of %d milliseconds", flags.debounceMS) + + // Watch for changes and trigger restartApp() + doWatcherLoop(buildOptions, debugBinaryProcess, flags, watcher, exitCodeChannel, quitChannel) + + // Kill the current program if running + if debugBinaryProcess != nil { + err := debugBinaryProcess.Kill() + if err != nil { + return err + } + } + + // Remove dev binary + err = os.Remove(appBinary) + if err != nil { + return err + } + + LogGreen("Development mode exited") + + return nil + }) + return nil +} + +func syncGoModVersion(cwd string) error { + gomodFilename := filepath.Join(cwd, "go.mod") + gomodData, err := os.ReadFile(gomodFilename) + if err != nil { + return err + } + outOfSync, err := gomod.GoModOutOfSync(gomodData, internal.Version) + if err != nil { + return err + } + if !outOfSync { + return nil + } + LogGreen("Updating go.mod to use Wails '%s'", internal.Version) + newGoData, err := gomod.UpdateGoModVersion(gomodData, internal.Version) + if err != nil { + return err + } + return os.WriteFile(gomodFilename, newGoData, 0755) +} + +func runCommand(dir string, exitOnError bool, command string, args ...string) error { + LogGreen("Executing: " + command + " " + strings.Join(args, " ")) + cmd := exec.Command(command, args...) + cmd.Dir = dir + output, err := cmd.CombinedOutput() + if err != nil { + println(string(output)) + if exitOnError { + os.Exit(1) + } + return err + } + return nil +} + +// defaultDevFlags generates devFlags with default options +func defaultDevFlags() devFlags { + return devFlags{ + devServerURL: defaultDevServerURL, + compilerCommand: "go", + verbosity: 1, + extensions: "go", + debounceMS: 100, + } +} + +// generateBuildOptions creates a build.Options using the flags +func generateBuildOptions(flags devFlags) *build.Options { + result := &build.Options{ + OutputType: "dev", + Mode: build.Dev, + Arch: runtime.GOARCH, + Pack: false, + Platform: runtime.GOOS, + LDFlags: flags.ldflags, + Compiler: flags.compilerCommand, + ForceBuild: flags.forceBuild, + IgnoreFrontend: false, + Verbosity: flags.verbosity, + WailsJSDir: flags.wailsjsdir, + } + switch runtime.GOOS { + case "darwin": + result.Pack = false + } + return result +} + +// loadAndMergeProjectConfig reconciles flags passed to the CLI with project config settings and updates +// the project config if necessary +func loadAndMergeProjectConfig(cwd string, flags *devFlags) (*project.Project, error) { + projectConfig, err := project.Load(cwd) + if err != nil { + return nil, err + } + + var shouldSaveConfig bool + + if projectConfig.AssetDirectory == "" && flags.assetDir == "" { + return nil, fmt.Errorf("No asset directory provided. Please use -assetdir to indicate which directory contains your built assets.") + } + + if flags.assetDir == "" && projectConfig.AssetDirectory != "" { + flags.assetDir = projectConfig.AssetDirectory + } + + if flags.assetDir != projectConfig.AssetDirectory { + projectConfig.AssetDirectory = filepath.ToSlash(flags.assetDir) + } + + flags.assetDir, err = filepath.Abs(flags.assetDir) + if err != nil { + return nil, err + } + + if flags.devServerURL == defaultDevServerURL && projectConfig.DevServerURL != defaultDevServerURL && projectConfig.DevServerURL != "" { + flags.devServerURL = projectConfig.DevServerURL + } + + if flags.devServerURL != projectConfig.DevServerURL { + projectConfig.DevServerURL = flags.devServerURL + shouldSaveConfig = true + } + + if flags.wailsjsdir == "" && projectConfig.WailsJSDir != "" { + flags.wailsjsdir = projectConfig.WailsJSDir + } + + if flags.wailsjsdir == "" { + flags.wailsjsdir = "./frontend" + } + + if flags.wailsjsdir != projectConfig.WailsJSDir { + projectConfig.WailsJSDir = filepath.ToSlash(flags.wailsjsdir) + shouldSaveConfig = true + } + + if flags.debounceMS == 100 && projectConfig.DebounceMS != 100 { + if projectConfig.DebounceMS == 0 { + projectConfig.DebounceMS = 100 + } + flags.debounceMS = projectConfig.DebounceMS + } + + if flags.debounceMS != projectConfig.DebounceMS { + projectConfig.DebounceMS = flags.debounceMS + shouldSaveConfig = true + } + + if shouldSaveConfig { + err = projectConfig.Save() + if err != nil { + return nil, err + } + } + + return projectConfig, nil +} + +// runFrontendDevCommand will run the `frontend:dev` command if it was given, ex- `npm run dev` +func runFrontendDevCommand(cwd string, devCommand string, wg *sync.WaitGroup) func(group *sync.WaitGroup) { + LogGreen("Running frontend dev command: '%s'", devCommand) + ctx, cancel := context.WithCancel(context.Background()) + dir := filepath.Join(cwd, "frontend") + cmdSlice := strings.Split(devCommand, " ") + wg.Add(1) + cmd := exec.CommandContext(ctx, cmdSlice[0], cmdSlice[1:]...) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + cmd.Dir = dir + go func(ctx context.Context, devCommand string, cwd string, wg *sync.WaitGroup) { + err := cmd.Run() + if err != nil { + if err.Error() != "exit status 1" { + LogRed("Error from '%s': %s", devCommand, err.Error()) + } + } + LogGreen("Dev command exited!") + wg.Done() + }(ctx, devCommand, cwd, wg) + + return func(wg *sync.WaitGroup) { + if runtime.GOOS == "windows" { + // Credit: https://stackoverflow.com/a/44551450 + // For whatever reason, killing an npm script on windows just doesn't exit properly with cancel + if cmd != nil && cmd.Process != nil { + kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid)) + kill.Stderr = os.Stderr + kill.Stdout = os.Stdout + err := kill.Run() + if err != nil { + if err.Error() != "exit status 1" { + LogRed("Error from '%s': %s", devCommand, err.Error()) + } + } + } + } else { + cancel() + } + wg.Wait() + } +} + +// initialiseWatcher creates the project directory watcher that will trigger recompile +func initialiseWatcher(cwd string, logFatal func(string, ...interface{})) (*fsnotify.Watcher, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, err + } + + // Get all subdirectories + dirs, err := fs.GetSubdirectories(cwd) + if err != nil { + return nil, err + } + + // Setup a watcher for non-node_modules directories + dirs.Each(func(dir string) { + if strings.Contains(dir, "node_modules") { + return + } + // Ignore build directory + if strings.HasPrefix(dir, filepath.Join(cwd, "build")) { + return + } + // Ignore dot directories + if strings.HasPrefix(dir, ".") { + return + } + err = watcher.Add(dir) + if err != nil { + logFatal(err.Error()) + } + }) + return watcher, nil +} + +// restartApp does the actual rebuilding of the application when files change +func restartApp(buildOptions *build.Options, debugBinaryProcess *process.Process, flags devFlags, exitCodeChannel chan int) (*process.Process, string, error) { + + appBinary, err := build.Build(buildOptions) + println() + if err != nil { + LogRed("Build error - continuing to run current version") + LogDarkYellow(err.Error()) + return nil, "", nil + } + + // Kill existing binary if need be + if debugBinaryProcess != nil { + killError := debugBinaryProcess.Kill() + + if killError != nil { + buildOptions.Logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID()) + } + + debugBinaryProcess = nil + } + + // Start up new binary with correct args + args := slicer.StringSlicer{} + args.Add("-loglevel", flags.loglevel) + if flags.assetDir != "" { + args.Add("-assetdir", flags.assetDir) + } + if flags.devServerURL != "" { + args.Add("-devserverurl", flags.devServerURL) + } + newProcess := process.NewProcess(appBinary, args.AsSlice()...) + err = newProcess.Start(exitCodeChannel) + if err != nil { + // Remove binary + if fs.FileExists(appBinary) { + deleteError := fs.DeleteFile(appBinary) + if deleteError != nil { + buildOptions.Logger.Fatal("Unable to delete app binary: " + appBinary) + } + } + buildOptions.Logger.Fatal("Unable to start application: %s", err.Error()) + } + + return newProcess, appBinary, nil +} + +// doWatcherLoop is the main watch loop that runs while dev is active +func doWatcherLoop(buildOptions *build.Options, debugBinaryProcess *process.Process, flags devFlags, watcher *fsnotify.Watcher, exitCodeChannel chan int, quitChannel chan os.Signal) { + // Main Loop + var ( + err error + newBinaryProcess *process.Process + ) + var extensionsThatTriggerARebuild = sliceToMap(strings.Split(flags.extensions, ",")) + quit := false + interval := time.Duration(flags.debounceMS) * time.Millisecond + timer := time.NewTimer(interval) + rebuild := false + reload := false + for quit == false { + //reload := false + select { + case exitCode := <-exitCodeChannel: + if exitCode == 0 { + quit = true + } + case item := <-watcher.Events: + // Check for file writes + if item.Op&fsnotify.Write == fsnotify.Write { + // Ignore directories + if fs.DirExists(item.Name) { + continue + } + + // Iterate all file patterns + ext := filepath.Ext(item.Name) + if ext != "" { + ext = ext[1:] + if _, exists := extensionsThatTriggerARebuild[ext]; exists { + rebuild = true + timer.Reset(interval) + continue + } + } + + if strings.HasPrefix(item.Name, flags.assetDir) { + reload = true + } + timer.Reset(interval) + } + // Check for new directories + if item.Op&fsnotify.Create == fsnotify.Create { + // If this is a folder, add it to our watch list + if fs.DirExists(item.Name) { + //node_modules is BANNED! + if !strings.Contains(item.Name, "node_modules") { + err := watcher.Add(item.Name) + if err != nil { + buildOptions.Logger.Fatal("%s", err.Error()) + } + LogGreen("Added new directory to watcher: %s", item.Name) + } + } + } + case <-timer.C: + if rebuild { + rebuild = false + LogGreen("[Rebuild triggered] files updated") + // Try and build the app + newBinaryProcess, _, err = restartApp(buildOptions, debugBinaryProcess, flags, exitCodeChannel) + if err != nil { + LogRed("Error during build: %s", err.Error()) + continue + } + // If we have a new process, save it + if newBinaryProcess != nil { + debugBinaryProcess = newBinaryProcess + } + } + if reload { + reload = false + _, err = http.Get("http://localhost:34115/wails/reload") + if err != nil { + LogRed("Error during refresh: %s", err.Error()) + } + } + case <-quitChannel: + LogGreen("\nCaught quit") + quit = true + } + } +} diff --git a/v2/cmd/wails/internal/commands/doctor/doctor.go b/v2/cmd/wails/internal/commands/doctor/doctor.go new file mode 100644 index 000000000..0075a7efb --- /dev/null +++ b/v2/cmd/wails/internal/commands/doctor/doctor.go @@ -0,0 +1,158 @@ +package doctor + +import ( + "fmt" + "io" + "os" + "runtime" + "strings" + "text/tabwriter" + + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/internal/system" + "github.com/wailsapp/wails/v2/internal/system/packagemanager" + "github.com/wailsapp/wails/v2/pkg/clilogger" +) + +// AddSubcommand adds the `doctor` command for the Wails application +func AddSubcommand(app *clir.Cli, w io.Writer) error { + + command := app.NewSubCommand("doctor", "Diagnose your environment") + + command.Action(func() error { + + logger := clilogger.New(w) + + app.PrintBanner() + + logger.Print("Scanning system - Please wait (this may take a long time)...") + + // Get system info + info, err := system.GetInfo() + if err != nil { + logger.Println("Failed.") + return err + } + logger.Println("Done.") + + logger.Println("") + + // Start a new tabwriter + w := new(tabwriter.Writer) + w.Init(os.Stdout, 8, 8, 0, '\t', 0) + + // Write out the system information + fmt.Fprintf(w, "System\n") + fmt.Fprintf(w, "------\n") + fmt.Fprintf(w, "%s\t%s\n", "OS:", info.OS.Name) + fmt.Fprintf(w, "%s\t%s\n", "Version: ", info.OS.Version) + fmt.Fprintf(w, "%s\t%s\n", "ID:", info.OS.ID) + + // Output Go Information + fmt.Fprintf(w, "%s\t%s\n", "Go Version:", runtime.Version()) + fmt.Fprintf(w, "%s\t%s\n", "Platform:", runtime.GOOS) + fmt.Fprintf(w, "%s\t%s\n", "Architecture:", runtime.GOARCH) + + // Exit early if PM not found + if info.PM != nil { + fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name()) + } + + // Output Dependencies Status + var dependenciesMissing = []string{} + var externalPackages = []*packagemanager.Dependancy{} + var dependenciesAvailableRequired = 0 + var dependenciesAvailableOptional = 0 + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "Dependency\tPackage Name\tStatus\tVersion\n") + fmt.Fprintf(w, "----------\t------------\t------\t-------\n") + + hasOptionalDependencies := false + // Loop over dependencies + for _, dependency := range info.Dependencies { + + name := dependency.Name + if dependency.Optional { + name = "*" + name + hasOptionalDependencies = true + } + packageName := "Unknown" + status := "Not Found" + + // If we found the package + if dependency.PackageName != "" { + + packageName = dependency.PackageName + + // If it's installed, update the status + if dependency.Installed { + status = "Installed" + } else { + // Generate meaningful status text + status = "Available" + + if dependency.Optional { + dependenciesAvailableOptional++ + } else { + dependenciesAvailableRequired++ + } + } + } else { + if !dependency.Optional { + dependenciesMissing = append(dependenciesMissing, dependency.Name) + } + + if dependency.External { + externalPackages = append(externalPackages, dependency) + } + } + + fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, packageName, status, dependency.Version) + } + if hasOptionalDependencies { + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "* - Optional Dependency\n") + } + w.Flush() + logger.Println("") + logger.Println("Diagnosis") + logger.Println("---------") + + // Generate an appropriate diagnosis + + if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 { + logger.Println("Your system is ready for Wails development!") + } else { + logger.Println("Your system has missing dependencies!\n") + } + + if dependenciesAvailableRequired != 0 { + logger.Println("Required package(s) installation details: \n" + info.Dependencies.InstallAllRequiredCommand()) + } + + if dependenciesAvailableOptional != 0 { + logger.Println("Optional package(s) installation details: \n" + info.Dependencies.InstallAllOptionalCommand()) + } + // + //if len(externalPackages) > 0 { + // for _, p := range externalPackages { + // if p.Optional { + // print("[Optional] ") + // } + // logger.Println("Install " + p.Name + ": " + p.InstallCommand) + // } + //} + + if len(dependenciesMissing) != 0 { + // TODO: Check if apps are available locally and if so, adjust the diagnosis + logger.Println("Fatal:") + logger.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " ")) + logger.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages") + } + + logger.Println("") + return nil + }) + + return nil +} diff --git a/v2/cmd/wails/internal/commands/generate/README.md b/v2/cmd/wails/internal/commands/generate/README.md new file mode 100644 index 000000000..c24eb060a --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/README.md @@ -0,0 +1,18 @@ +# Generate + +The `generate` command provides the ability to generate various Wails related components. + +## Usage + +`wails generate [subcommand] [options]` + +## Template + +`wails generate template -name [-frontend] [-q]` + +Generate a starter template for you to customise. + +| Flag | Details | +| :------------- | :----------- | +| -frontend | Copies all the files from the current directory into the template's `frontend` directory. Useful for converting frontend projects created by boilerplate generators. | +| -q | Suppress output | diff --git a/v2/cmd/wails/internal/commands/generate/generate.go b/v2/cmd/wails/internal/commands/generate/generate.go new file mode 100644 index 000000000..58f623bf0 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/generate.go @@ -0,0 +1,23 @@ +package generate + +import ( + "io" + + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate/template" + + "github.com/leaanthony/clir" +) + +// AddSubcommand adds the `generate` command for the Wails application +func AddSubcommand(app *clir.Cli, w io.Writer) error { + + command := app.NewSubCommand("generate", "Code Generation Tools") + + err := AddModuleCommand(app, command, w) + if err != nil { + return err + } + template.AddSubCommand(app, command, w) + + return nil +} diff --git a/v2/cmd/wails/internal/commands/generate/module.go b/v2/cmd/wails/internal/commands/generate/module.go new file mode 100644 index 000000000..91f9309f9 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/module.go @@ -0,0 +1,58 @@ +package generate + +import ( + "fmt" + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/cmd/wails/internal" + "github.com/wailsapp/wails/v2/internal/shell" + "io" + "os" + "path/filepath" + "runtime" + "strings" +) + +// AddModuleCommand adds the `module` subcommand for the `generate` command +func AddModuleCommand(app *clir.Cli, parent *clir.Command, w io.Writer) error { + + command := parent.NewSubCommand("module", "Generate wailsjs modules") + var tags string + command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags) + + command.Action(func() error { + + filename := "wailsbindings" + if runtime.GOOS == "windows" { + filename += ".exe" + } + // go build -tags bindings -o bindings.exe + tempDir := os.TempDir() + filename = filepath.Join(tempDir, filename) + + cwd, err := os.Getwd() + if err != nil { + return err + } + + tagList := internal.ParseUserTags(tags) + tagList = append(tagList, "bindings") + + stdout, stderr, err := shell.RunCommand(cwd, "go", "build", "-tags", strings.Join(tagList, ","), "-o", filename) + if err != nil { + return fmt.Errorf("%s\n%s\n%s", stdout, stderr, err) + } + + stdout, stderr, err = shell.RunCommand(cwd, filename) + if err != nil { + return fmt.Errorf("%s\n%s\n%s", stdout, stderr, err) + } + + err = os.Remove(filename) + if err != nil { + return err + } + + return nil + }) + return nil +} diff --git a/v2/cmd/wails/internal/commands/generate/template/base/NEXTSTEPS.md.template b/v2/cmd/wails/internal/commands/generate/template/base/NEXTSTEPS.md.template new file mode 100644 index 000000000..a357acd82 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/NEXTSTEPS.md.template @@ -0,0 +1,42 @@ +# Next Steps + +Congratulations on generating your template! + +## Completing your template + +The next steps to complete the template are: + + 1. Complete the fields in the `template.json` file. + - It is really important to ensure `helpurl` is valid as this is where users of the template will be directed for help. + 2. Update `README.md`. + 3. Edit `wails.json` and ensure all fields are correct, especially: + - `assetdir` - path to your assets + - `wailsjsdir` - path to generate wailsjs modules + - `frontend:install` - The command to install your frontend dependencies + - `frontend:build` - The command to build your frontend + 4. Remove any `public` or `dist` directories. + 5. Delete this file. + +## Testing your template + +You can test your template by running this command: + +`wails init -n test -t {{.TemplateDir}}` + +### Checklist + +Once generated, do the following tests: + - Change into the new project directory and run `wails build`. A working binary should be generated in the `build/bin` project directory. + - Run `wails dev`. This will compile your app and run it. + - You should be able to see your application running on http://localhost:34115/ + +## Publishing your template + +You can publish a template to a git repository and use it as follows: + +`wails init -name test -t https://your/git/url` + +EG: + +`wails init -name test -t https://github.com/leaanthony/testtemplate` + diff --git a/v2/cmd/wails/internal/commands/generate/template/base/README.md b/v2/cmd/wails/internal/commands/generate/template/base/README.md new file mode 100644 index 000000000..fd993210d --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/README.md @@ -0,0 +1,16 @@ +# README + +## About + +About your template + +## Building + +To build this project in debug mode, use `wails build`. For production, use `wails build -production`. +To generate a platform native package, add the `-package` flag. + +## Live Development + +To run in live development mode, run `wails dev` in the project directory. In another terminal, go into the `frontend` +directory and run `npm run dev`. The frontend dev server will run on http://localhost:34115. Connect to this +in your browser and connect to your application. diff --git a/v2/cmd/wails/internal/commands/generate/template/base/app.tmpl.go b/v2/cmd/wails/internal/commands/generate/template/base/app.tmpl.go new file mode 100644 index 000000000..dd540dcd2 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/app.tmpl.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + "fmt" +) + +// App struct +type App struct { + ctx context.Context +} + +// NewApp creates a new App application struct +func NewApp() *App { + return &App{} +} + +// startup is called at application startup +func (a *App) startup(ctx context.Context) { + // Perform your setup here + a.ctx = ctx +} + +// domReady is called after the front-end dom has been loaded +func (a App) domReady(ctx context.Context) { + // Add your action here +} + +// shutdown is called at application termination +func (a *App) shutdown(ctx context.Context) { + // Perform your teardown here +} + +// Greet returns a greeting for the given name +func (a *App) Greet(name string) string { + return fmt.Sprintf("Hello %s!", name) +} diff --git a/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/fonts/OFL.txt b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/fonts/OFL.txt new file mode 100644 index 000000000..9cac04ce8 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com), + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/fonts/nunito-v16-latin-regular.woff2 b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/fonts/nunito-v16-latin-regular.woff2 new file mode 100644 index 000000000..2f9cc5964 Binary files /dev/null and b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/fonts/nunito-v16-latin-regular.woff2 differ diff --git a/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/images/logo-dark.svg b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/images/logo-dark.svg new file mode 100644 index 000000000..855d7e0ef --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/assets/images/logo-dark.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/index.html b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/index.html new file mode 100644 index 000000000..7f516f7fa --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/index.html @@ -0,0 +1,18 @@ + + + + + + + + +
Please enter your name below 👇
+
+ + +
+ + + + + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/main.css b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/main.css new file mode 100644 index 000000000..af6412466 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/main.css @@ -0,0 +1,72 @@ +html { + background-color: rgba(33, 37, 43, 1); + text-align: center; + color: white; +} + +body { + margin: 0; + color: white; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; +} + +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + src: local(""), + url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2"); +} + +.logo { + display: block; + width: 35%; + height: 35%; + margin: auto; + padding: 15% 0 0; + background-position: center; + background-repeat: no-repeat; + background-image: url("./assets/images/logo-dark.svg"); +} +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; +} +.input-box .btn { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} diff --git a/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/main.js b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/main.js new file mode 100644 index 000000000..db404e459 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/frontend/dist/main.js @@ -0,0 +1,23 @@ +// Get input + focus +let nameElement = document.getElementById("name"); +nameElement.focus(); + +// Setup the greet function +window.greet = function () { + + // Get name + let name = nameElement.value; + + // Call App.Greet(name) + window.go.main.App.Greet(name).then((result) => { + // Update result with data back from App.Greet() + document.getElementById("result").innerText = result; + }); +}; + +nameElement.onkeydown = function (e) { + console.log(e) + if (e.keyCode == 13) { + window.greet() + } +} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/generate/template/base/frontend/package.tmpl.json b/v2/cmd/wails/internal/commands/generate/template/base/frontend/package.tmpl.json new file mode 100644 index 000000000..01780288d --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/frontend/package.tmpl.json @@ -0,0 +1,12 @@ +{ + "name": "{{.ProjectName}}", + "version": "1.0.0", + "description": "", + "main": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "" + }, + "keywords": [], + "author": "{{.AuthorName}}" +} diff --git a/v2/cmd/wails/internal/commands/generate/template/base/go.mod.tmpl b/v2/cmd/wails/internal/commands/generate/template/base/go.mod.tmpl new file mode 100644 index 000000000..166b9252d --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/go.mod.tmpl @@ -0,0 +1,38 @@ +module changeme + +go 1.17 + +require github.com/wailsapp/wails/v2 {{.WailsVersion}} + +require ( +github.com/andybalholm/brotli v1.0.2 // indirect +github.com/davecgh/go-spew v1.1.1 // indirect +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect +github.com/gabriel-vasile/mimetype v1.3.1 // indirect +github.com/go-ole/go-ole v1.2.5 // indirect +github.com/gofiber/fiber/v2 v2.17.0 // indirect +github.com/gofiber/websocket/v2 v2.0.8 // indirect +github.com/google/uuid v1.1.2 // indirect +github.com/imdario/mergo v0.3.12 // indirect +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect +github.com/klauspost/compress v1.12.2 // indirect +github.com/leaanthony/debme v1.2.1 // indirect +github.com/leaanthony/go-ansi-parser v1.0.1 // indirect +github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect +github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect +github.com/leaanthony/slicer v1.5.0 // indirect +github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect +github.com/leaanthony/webview2runtime v1.1.0 // indirect +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect +github.com/pkg/errors v0.9.1 // indirect +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect +github.com/tkrajina/go-reflector v0.5.5 // indirect +github.com/valyala/bytebufferpool v1.0.0 // indirect +github.com/valyala/fasthttp v1.28.0 // indirect +github.com/valyala/tcplisten v1.0.0 // indirect +golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect +) + +// replace github.com/wailsapp/wails/v2 {{.WailsVersion}} => {{.WailsDirectory}} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/generate/template/base/go.sum b/v2/cmd/wails/internal/commands/generate/template/base/go.sum new file mode 100644 index 000000000..8e8bd03fd --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/go.sum @@ -0,0 +1,225 @@ +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab h1:9e2joQGp642wHGFP5m86SDptAavrdGBe8/x9DGEEAaI= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= +github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gofiber/fiber/v2 v2.17.0 h1:qP3PkGUbBB0i9iQh5E057XI1yO5CZigUxZhyUFYAFoM= +github.com/gofiber/fiber/v2 v2.17.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg= +github.com/gofiber/websocket/v2 v2.0.8 h1:Hb4y6IxYZVMO0segROODXJiXVgVD3a6i7wnfot8kM6k= +github.com/gofiber/websocket/v2 v2.0.8/go.mod h1:fv8HSGQX09sauNv9g5Xq8GeGAaahLFYQKKb4ZdT0x2w= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 h1:pdFFlHXY9tZXmJz+tRSm1DzYEH4ebha7cffmm607bMU= +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8= +github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +github.com/leaanthony/debme v1.1.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= +github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= +github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= +github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4= +github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= +github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= +github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= +github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 h1:nK/JTPyJi5QRqYjVZjXgtN4/dhg2qtngoLxLDVn429k= +github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM= +github.com/leaanthony/gosod v1.0.2/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU= +github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= +github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= +github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= +github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= +github.com/leaanthony/webview2runtime v1.1.0 h1:N0pv55ift8XtqozIp4PNOtRCJ/Qdd/qzx80lUpalS4c= +github.com/leaanthony/webview2runtime v1.1.0/go.mod h1:hH9GnWCve3DYzNaPOcPbhHQ7fodXR1QJNsnwixid4Tk= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs= +github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tc-hib/winres v0.1.5/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A= +github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= +github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs= +github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= +github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4= +github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28= +github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/v2/cmd/wails/internal/commands/generate/template/base/main.tmpl.go b/v2/cmd/wails/internal/commands/generate/template/base/main.tmpl.go new file mode 100644 index 000000000..4bec207bd --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/main.tmpl.go @@ -0,0 +1,54 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/logger" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) + +//go:embed frontend/dist +var assets embed.FS + +func main() { + // Create an instance of the app structure + app := NewApp() + + // Create application with options + err := wails.Run(&options.App{ + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + // MinWidth: 720, + // MinHeight: 570, + // MaxWidth: 1280, + // MaxHeight: 740, + DisableResize: false, + Fullscreen: false, + Frameless: false, + StartHidden: false, + HideWindowOnClose: false, + RGBA: &options.RGBA{255, 255, 255, 255}, + Assets: assets, + LogLevel: logger.DEBUG, + OnStartup: app.startup, + OnDomReady: app.domReady, + OnShutdown: app.shutdown, + Bind: []interface{}{ + app, + }, + // Windows platform specific options + Windows: &windows.Options{ + WebviewIsTransparent: false, + WindowIsTranslucent: false, + DisableWindowIcon: false, + }, + }) + + if err != nil { + log.Fatal(err) + } +} diff --git a/v2/cmd/wails/internal/commands/generate/template/base/template.template.json b/v2/cmd/wails/internal/commands/generate/template/base/template.template.json new file mode 100644 index 000000000..bde089e00 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/template.template.json @@ -0,0 +1,7 @@ +{ + "name": "Long name", + "shortname": "{{.Name}}", + "author": "", + "description": "Description of the template", + "helpurl": "URL for help with the template, eg homepage" +} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/generate/template/base/wails.tmpl.json b/v2/cmd/wails/internal/commands/generate/template/base/wails.tmpl.json new file mode 100644 index 000000000..b8d08108d --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/base/wails.tmpl.json @@ -0,0 +1,11 @@ +{ + "name": "{{.ProjectName}}", + "outputfilename": "{{.BinaryName}}", + "assetdir": "frontend/dist", + "frontend:install": "npm install", + "frontend:build": "npm run build", + "author": { + "name": "{{.AuthorName}}", + "email": "{{.AuthorEmail}}" + } +} diff --git a/v2/cmd/wails/internal/commands/generate/template/template.go b/v2/cmd/wails/internal/commands/generate/template/template.go new file mode 100644 index 000000000..6464ff7c4 --- /dev/null +++ b/v2/cmd/wails/internal/commands/generate/template/template.go @@ -0,0 +1,215 @@ +package template + +import ( + "embed" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/leaanthony/debme" + + "github.com/leaanthony/gosod" + "github.com/wailsapp/wails/v2/internal/fs" + + "github.com/leaanthony/clir" + "github.com/tidwall/sjson" +) + +//go:embed base +var base embed.FS + +func AddSubCommand(app *clir.Cli, parent *clir.Command, w io.Writer) { + + // command + command := parent.NewSubCommand("template", "Generates a wails template") + + name := "" + command.StringFlag("name", "The name of the template", &name) + + existingProjectDir := "" + command.StringFlag("frontend", "A path to an existing frontend project to include in the template", &existingProjectDir) + + // Quiet Init + quiet := false + command.BoolFlag("q", "Suppress output to console", &quiet) + + command.Action(func() error { + + // name is mandatory + if name == "" { + command.PrintHelp() + return fmt.Errorf("no template name given") + } + + // If the current directory is not empty, we create a new directory + cwd, err := os.Getwd() + if err != nil { + return err + } + templateDir := filepath.Join(cwd, name) + if !fs.DirExists(templateDir) { + err := os.MkdirAll(templateDir, 0755) + if err != nil { + return err + } + } + empty, err := fs.DirIsEmpty(templateDir) + if err != nil { + return err + } + if !empty { + templateDir = filepath.Join(cwd, name) + println("Creating new template directory:", name) + err = fs.Mkdir(templateDir) + if err != nil { + return err + } + } + + // Create base template + baseTemplate, err := debme.FS(base, "base") + if err != nil { + return err + } + g := gosod.New(baseTemplate) + g.SetTemplateFilters([]string{".template"}) + + err = os.Chdir(templateDir) + if err != nil { + return err + } + + type templateData struct { + Name string + Description string + TemplateDir string + WailsVersion string + } + + println("Extracting base template files...") + + err = g.Extract(templateDir, &templateData{ + Name: name, + TemplateDir: templateDir, + WailsVersion: app.Version(), + }) + if err != nil { + return err + } + + err = os.Chdir(cwd) + if err != nil { + return err + } + + // If we aren't migrating the files, just exit + if existingProjectDir == "" { + return nil + } + + // Remove frontend directory + frontendDir := filepath.Join(templateDir, "frontend") + err = os.RemoveAll(frontendDir) + if err != nil { + return err + } + + // Copy the files into a new frontend directory + println("Migrating existing project files to frontend directory...") + + sourceDir, err := filepath.Abs(existingProjectDir) + if err != nil { + return err + } + + newFrontendDir := filepath.Join(templateDir, "frontend") + err = fs.CopyDirExtended(sourceDir, newFrontendDir, []string{name, "node_modules"}) + if err != nil { + return err + } + + // Process package.json + err = processPackageJSON(frontendDir) + if err != nil { + return err + } + + // Process package-lock.json + err = processPackageLockJSON(frontendDir) + if err != nil { + return err + } + + // Remove node_modules - ignore error, eg it doesn't exist + _ = os.RemoveAll(filepath.Join(frontendDir, "node_modules")) + + return nil + + }) +} + +func processPackageJSON(frontendDir string) error { + var err error + + packageJSON := filepath.Join(frontendDir, "package.json") + if !fs.FileExists(packageJSON) { + println("No package.json found - cannot process.") + return nil + } + + data, err := os.ReadFile(packageJSON) + if err != nil { + return err + } + json := string(data) + + // We will ignore these errors - it's not critical + println("Updating package.json data...") + json, _ = sjson.Set(json, "name", "{{.ProjectName}}") + json, _ = sjson.Set(json, "author", "{{.AuthorName}}") + + err = os.WriteFile(packageJSON, []byte(json), 0644) + if err != nil { + return err + } + baseDir := filepath.Dir(packageJSON) + println("Renaming package.json -> package.tmpl.json...") + err = os.Rename(packageJSON, filepath.Join(baseDir, "package.tmpl.json")) + if err != nil { + return err + } + return nil +} + +func processPackageLockJSON(frontendDir string) error { + var err error + + filename := filepath.Join(frontendDir, "package-lock.json") + if !fs.FileExists(filename) { + println("No package-lock.json found - cannot process.") + return nil + } + + data, err := os.ReadFile(filename) + if err != nil { + return err + } + json := string(data) + + // We will ignore these errors - it's not critical + println("Updating package-lock.json data...") + json, _ = sjson.Set(json, "name", "{{.ProjectName}}") + + err = os.WriteFile(filename, []byte(json), 0644) + if err != nil { + return err + } + baseDir := filepath.Dir(filename) + println("Renaming package-lock.json -> package-lock.tmpl.json...") + err = os.Rename(filename, filepath.Join(baseDir, "package-lock.tmpl.json")) + if err != nil { + return err + } + return nil +} diff --git a/v2/cmd/wails/internal/commands/initialise/initialise.go b/v2/cmd/wails/internal/commands/initialise/initialise.go new file mode 100644 index 000000000..f87e6b7a3 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/initialise.go @@ -0,0 +1,235 @@ +package initialise + +import ( + "fmt" + "github.com/flytam/filenamify" + "github.com/leaanthony/slicer" + "io" + "os" + "os/exec" + "path/filepath" + "strings" + "time" + + "github.com/wailsapp/wails/v2/pkg/buildassets" + + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates" + + "github.com/leaanthony/clir" + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/pkg/clilogger" + "github.com/wailsapp/wails/v2/pkg/git" +) + +// AddSubcommand adds the `init` command for the Wails application +func AddSubcommand(app *clir.Cli, w io.Writer) error { + + command := app.NewSubCommand("init", "Initialise a new Wails project") + + // Setup template name flag + templateName := "vanilla" + description := "Name of built-in template to use, path to template or template url." + command.StringFlag("t", description, &templateName) + + // Setup project name + projectName := "" + command.StringFlag("n", "Name of project", &projectName) + + // Setup project directory + projectDirectory := "" + command.StringFlag("d", "Project directory", &projectDirectory) + + // Quiet Init + quiet := false + command.BoolFlag("q", "Suppress output to console", &quiet) + + initGit := false + gitInstalled := git.IsInstalled() + if gitInstalled { + // Git Init + command.BoolFlag("g", "Initialise git repository", &initGit) + } + + // VSCode project files + ide := "" + command.StringFlag("ide", "Generate IDE project files", &ide) + + // List templates + list := false + command.BoolFlag("l", "List templates", &list) + + command.Action(func() error { + + // Create logger + logger := clilogger.New(w) + logger.Mute(quiet) + + // Are we listing templates? + if list { + app.PrintBanner() + err := templates.OutputList(logger) + logger.Println("") + return err + } + + // Validate name + if len(projectName) == 0 { + logger.Println("ERROR: Project name required") + logger.Println("") + command.PrintHelp() + return nil + } + + // Validate IDE option + supportedIDEs := slicer.String([]string{"vscode", "goland"}) + ide = strings.ToLower(ide) + if ide != "" { + if !supportedIDEs.Contains(ide) { + return fmt.Errorf("ide '%s' not supported. Valid values: %s", ide, supportedIDEs.Join(" ")) + } + } + + if !quiet { + app.PrintBanner() + } + + task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName)) + logger.Println(task) + logger.Println(strings.Repeat("-", len(task))) + + projectFilename, err := filenamify.Filenamify(projectName, filenamify.Options{ + Replacement: "_", + MaxLength: 255, + }) + if err != nil { + return err + } + goBinary, err := exec.LookPath("go") + if err != nil { + return fmt.Errorf("unable to find Go compiler. Please download and install Go: https://golang.org/dl/") + } + + // Get base path and convert to forward slashes + goPath := filepath.ToSlash(filepath.Dir(goBinary)) + // Trim bin directory + goSDKPath := strings.TrimSuffix(goPath, "/bin") + + // Create Template Options + options := &templates.Options{ + ProjectName: projectName, + TargetDir: projectDirectory, + TemplateName: templateName, + Logger: logger, + IDE: ide, + InitGit: initGit, + ProjectNameFilename: projectFilename, + WailsVersion: app.Version(), + GoSDKPath: goSDKPath, + } + + // Try to discover author details from git config + findAuthorDetails(options) + + return initProject(options, quiet) + }) + + return nil +} + +// initProject is our main init command +func initProject(options *templates.Options, quiet bool) error { + + // Start Time + start := time.Now() + + // Install the template + remote, template, err := templates.Install(options) + if err != nil { + return err + } + + // Install the default assets + err = buildassets.Install(options.TargetDir, options.ProjectName) + if err != nil { + return err + } + + // Run `go mod tidy` to ensure `go.sum` is up to date + cmd := exec.Command("go", "mod", "tidy") + cmd.Dir = options.TargetDir + cmd.Stderr = os.Stderr + if !quiet { + println("") + cmd.Stdout = os.Stdout + } + err = cmd.Run() + if err != nil { + return err + } + + if options.InitGit { + err = initGit(options) + if err != nil { + return err + } + } + + if quiet { + return nil + } + + // Output stats + elapsed := time.Since(start) + options.Logger.Println("") + options.Logger.Println("Project Name: " + options.ProjectName) + options.Logger.Println("Project Directory: " + options.TargetDir) + options.Logger.Println("Project Template: " + options.TemplateName) + options.Logger.Println("Template Support: " + template.HelpURL) + + // IDE message + switch options.IDE { + case "vscode": + options.Logger.Println("VSCode config files generated.") + case "goland": + options.Logger.Println("Goland config files generated.") + } + + if options.InitGit { + options.Logger.Println("Git repository initialised.") + } + + if remote { + options.Logger.Println("\nNOTE: You have created a project using a remote template. The Wails project takes no responsibility for 3rd party templates. Only use remote templates that you trust.") + } + + options.Logger.Println("") + options.Logger.Println(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String())) + options.Logger.Println("") + + return nil +} + +func initGit(options *templates.Options) error { + err := git.InitRepo(options.TargetDir) + if err != nil { + return errors.Wrap(err, "Unable to initialise git repository:") + } + + return nil +} + +// findAuthorDetails tries to find the user's name and email +// from gitconfig. If it finds them, it stores them in the project options +func findAuthorDetails(options *templates.Options) { + if git.IsInstalled() { + name, err := git.Name() + if err == nil { + options.AuthorName = strings.TrimSpace(name) + } + + email, err := git.Email() + if err == nil { + options.AuthorEmail = strings.TrimSpace(email) + } + } +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/gitignore.txt b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/gitignore.txt new file mode 100644 index 000000000..73f69e095 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/gitignore.txt @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/modules.tmpl.xml b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/modules.tmpl.xml new file mode 100644 index 000000000..228a50071 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/modules.tmpl.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/name.tmpl b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/name.tmpl new file mode 100644 index 000000000..8c328a5d3 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/name.tmpl @@ -0,0 +1 @@ +{{.ProjectName}} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/projectname.iml b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/projectname.iml new file mode 100644 index 000000000..5e764c4f0 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/projectname.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/vcs.xml b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/vcs.xml new file mode 100644 index 000000000..78f5bc6f7 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/workspace.tmpl.xml b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/workspace.tmpl.xml new file mode 100644 index 000000000..42f98045b --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/goland/workspace.tmpl.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/launch.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/launch.tmpl.json new file mode 100644 index 000000000..d03d5ccdb --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/launch.tmpl.json @@ -0,0 +1,19 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Wails: Debug {{.ProjectName}}", + "type": "go", + "request": "launch", + "mode": "exec", + "program": "${workspaceFolder}/{{.PathToDesktopBinary}}", + "preLaunchTask": "build", + "cwd": "${workspaceFolder}", + "env": {}, + "args": [ + "-assetdir", + "{{.AssetDir}}" + ] + } + ] +} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/tasks.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/tasks.tmpl.json new file mode 100644 index 000000000..03c37e115 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/ides/vscode/tasks.tmpl.json @@ -0,0 +1,23 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "command": "go", + "args": [ + "build", + "-tags", + "dev", + "-gcflags", + "all=-N -l", + "-o", + "{{.PathToDesktopBinary}}" + ] + } + ] +} + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates.go b/v2/cmd/wails/internal/commands/initialise/templates/templates.go new file mode 100644 index 000000000..e01fecfb4 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates.go @@ -0,0 +1,442 @@ +package templates + +import ( + "embed" + "encoding/json" + "fmt" + "github.com/go-git/go-git/v5" + gofs "io/fs" + "io/ioutil" + "log" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/pkg/errors" + + "github.com/leaanthony/debme" + "github.com/leaanthony/gosod" + "github.com/olekukonko/tablewriter" + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/pkg/clilogger" +) + +//go:embed templates +var templates embed.FS + +//go:embed ides/* +var ides embed.FS + +// Cahce for the templates +// We use this because we need different views of the same data +var templateCache []Template = nil + +// Data contains the data we wish to embed during template installation +type Data struct { + ProjectName string + BinaryName string + WailsVersion string + NPMProjectName string + AuthorName string + AuthorEmail string + AuthorNameAndEmail string + WailsDirectory string + GoSDKPath string + AssetDir string + WindowsFlags string + CGOEnabled string + OutputFile string +} + +// Options for installing a template +type Options struct { + ProjectName string + TemplateName string + BinaryName string + TargetDir string + Logger *clilogger.CLILogger + PathToDesktopBinary string + PathToServerBinary string + InitGit bool + AuthorName string + AuthorEmail string + AssetDir string + IDE string + ProjectNameFilename string // The project name but as a valid filename + WailsVersion string + GoSDKPath string + WindowsFlags string + CGOEnabled string + OutputFile string +} + +// Template holds data relating to a template +// including the metadata stored in template.json +type Template struct { + + // Template details + Name string `json:"name"` + ShortName string `json:"shortname"` + Author string `json:"author"` + Description string `json:"description"` + HelpURL string `json:"helpurl"` + + // Other data + FS gofs.FS `json:"-"` +} + +func parseTemplate(template gofs.FS) (Template, error) { + var result Template + data, err := gofs.ReadFile(template, "template.json") + if err != nil { + return result, errors.Wrap(err, "Error parsing template") + } + err = json.Unmarshal(data, &result) + if err != nil { + return result, err + } + result.FS = template + return result, nil +} + +// List returns the list of available templates +func List() ([]Template, error) { + + // If the cache isn't loaded, load it + if templateCache == nil { + err := loadTemplateCache() + if err != nil { + return nil, err + } + } + + return templateCache, nil +} + +// getTemplateByShortname returns the template with the given short name +func getTemplateByShortname(shortname string) (Template, error) { + + var result Template + + // If the cache isn't loaded, load it + if templateCache == nil { + err := loadTemplateCache() + if err != nil { + return result, err + } + } + + for _, template := range templateCache { + if template.ShortName == shortname { + return template, nil + } + } + + return result, fmt.Errorf("shortname '%s' is not a valid template shortname", shortname) +} + +// Loads the template cache +func loadTemplateCache() error { + + templatesFS, err := debme.FS(templates, "templates") + if err != nil { + return err + } + + // Get directories + files, err := templatesFS.ReadDir(".") + if err != nil { + return err + } + + // Reset cache + templateCache = []Template{} + + for _, file := range files { + if file.IsDir() { + templateFS, err := templatesFS.FS(file.Name()) + if err != nil { + return err + } + template, err := parseTemplate(templateFS) + if err != nil { + // Cannot parse this template, continue + continue + } + templateCache = append(templateCache, template) + } + } + + return nil +} + +// Install the given template. Returns true if the template is remote. +func Install(options *Options) (bool, *Template, error) { + // Get cwd + cwd, err := os.Getwd() + if err != nil { + return false, nil, err + } + + // Did the user want to install in current directory? + if options.TargetDir == "" { + options.TargetDir = filepath.Join(cwd, options.ProjectName) + if fs.DirExists(options.TargetDir) { + return false, nil, fmt.Errorf("cannot create project directory. Dir exists: %s", options.TargetDir) + } + } else { + // Get the absolute path of the given directory + targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir)) + if err != nil { + return false, nil, err + } + options.TargetDir = targetDir + if !fs.DirExists(options.TargetDir) { + err := fs.Mkdir(options.TargetDir) + if err != nil { + return false, nil, err + } + } + } + + // Flag to indicate remote template + remoteTemplate := false + + // Is this a shortname? + template, err := getTemplateByShortname(options.TemplateName) + if err != nil { + // Is this a filepath? + templatePath, err := filepath.Abs(options.TemplateName) + if fs.DirExists(templatePath) { + templateFS := os.DirFS(templatePath) + template, err = parseTemplate(templateFS) + if err != nil { + return false, nil, errors.Wrap(err, "Error installing template") + } + } else { + // git clone to temporary dir + tempdir, err := gitclone(options) + defer func(path string) { + err := os.RemoveAll(path) + if err != nil { + log.Fatal(err) + } + }(tempdir) + if err != nil { + return false, nil, err + } + // Remove the .git directory + err = os.RemoveAll(filepath.Join(tempdir, ".git")) + if err != nil { + return false, nil, err + } + + templateFS := os.DirFS(tempdir) + template, err = parseTemplate(templateFS) + if err != nil { + return false, nil, err + } + remoteTemplate = true + } + } + + // Use Gosod to install the template + installer := gosod.New(template.FS) + + // Ignore template.json files + installer.IgnoreFile("template.json") + + // Setup the data. + // We use the directory name for the binary name, like Go + BinaryName := filepath.Base(options.TargetDir) + NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", "")) + localWailsDirectory := fs.RelativePath("../../../../../..") + + templateData := &Data{ + ProjectName: options.ProjectName, + BinaryName: filepath.Base(options.TargetDir), + NPMProjectName: NPMProjectName, + WailsDirectory: localWailsDirectory, + AuthorEmail: options.AuthorEmail, + AuthorName: options.AuthorName, + WailsVersion: options.WailsVersion, + GoSDKPath: options.GoSDKPath, + AssetDir: options.AssetDir, + } + + // Create a formatted name and email combo. + if options.AuthorName != "" { + templateData.AuthorNameAndEmail = options.AuthorName + " " + } + if options.AuthorEmail != "" { + templateData.AuthorNameAndEmail += "<" + options.AuthorEmail + ">" + } + templateData.AuthorNameAndEmail = strings.TrimSpace(templateData.AuthorNameAndEmail) + + installer.RenameFiles(map[string]string{ + "gitignore.txt": ".gitignore", + }) + + // Extract the template + err = installer.Extract(options.TargetDir, templateData) + if err != nil { + return false, nil, err + } + + err = generateIDEFiles(options) + if err != nil { + return false, nil, err + } + + return remoteTemplate, &template, nil +} + +// Clones the given uri and returns the temporary cloned directory +func gitclone(options *Options) (string, error) { + // Create temporary directory + dirname, err := ioutil.TempDir("", "wails-template-*") + if err != nil { + return "", err + } + _, err = git.PlainClone(dirname, false, &git.CloneOptions{ + URL: options.TemplateName, + }) + + return dirname, err + +} + +// OutputList prints the list of available tempaltes to the given logger +func OutputList(logger *clilogger.CLILogger) error { + templates, err := List() + if err != nil { + return err + } + + table := tablewriter.NewWriter(logger.Writer) + table.SetHeader([]string{"Template", "Short Name", "Description"}) + table.SetAutoWrapText(false) + table.SetAutoFormatHeaders(true) + table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetCenterSeparator("") + table.SetColumnSeparator("") + table.SetRowSeparator("") + table.SetHeaderLine(false) + table.SetBorder(false) + table.SetTablePadding("\t") // pad with tabs + table.SetNoWhiteSpace(true) + for _, template := range templates { + table.Append([]string{template.Name, template.ShortName, template.Description}) + } + table.Render() + return nil +} + +func generateIDEFiles(options *Options) error { + + switch options.IDE { + case "vscode": + return generateVSCodeFiles(options) + case "goland": + return generateGolandFiles(options) + } + + return nil +} + +type ideOptions struct { + name string + targetDir string + options *Options + renameFiles map[string]string + ignoredFiles []string +} + +func generateGolandFiles(options *Options) error { + ideoptions := ideOptions{ + name: "goland", + targetDir: filepath.Join(options.TargetDir, ".idea"), + options: options, + renameFiles: map[string]string{ + "projectname.iml": options.ProjectNameFilename + ".iml", + "gitignore.txt": ".gitignore", + "name": ".name", + }, + } + if !options.InitGit { + ideoptions.ignoredFiles = []string{"vcs.xml"} + } + err := installIDEFiles(ideoptions) + if err != nil { + return errors.Wrap(err, "generating Goland IDE files") + } + + return nil +} + +func generateVSCodeFiles(options *Options) error { + ideoptions := ideOptions{ + name: "vscode", + targetDir: filepath.Join(options.TargetDir, ".vscode"), + options: options, + } + return installIDEFiles(ideoptions) + +} + +func installIDEFiles(o ideOptions) error { + source, err := debme.FS(ides, "ides/"+o.name) + if err != nil { + return err + } + + // Use gosod to install the template + installer := gosod.New(source) + + if o.renameFiles != nil { + installer.RenameFiles(o.renameFiles) + } + + for _, ignoreFile := range o.ignoredFiles { + installer.IgnoreFile(ignoreFile) + } + + binaryName := filepath.Base(o.options.TargetDir) + if runtime.GOOS == "windows" { + // yay windows + binaryName += ".exe" + } + + // Parse wails.json for assetdir + wailsJSONBytes, err := os.ReadFile(filepath.Join(o.options.TargetDir, "wails.json")) + if err != nil { + return err + } + var wailsJSON map[string]interface{} + err = json.Unmarshal(wailsJSONBytes, &wailsJSON) + if err != nil { + return err + } + assetDir := wailsJSON["assetdir"] + if assetDir == "" { + return fmt.Errorf("Unable to find 'assetdir' in 'wails.json' ") + } + + o.options.AssetDir = assetDir.(string) + o.options.PathToDesktopBinary = filepath.ToSlash(filepath.Join("build", "bin", binaryName)) + + o.options.WindowsFlags = "" + o.options.CGOEnabled = "1" + if runtime.GOOS == "windows" { + o.options.WindowsFlags = " -H windowsgui" + o.options.CGOEnabled = "0" + } + err = installer.Extract(o.targetDir, o.options) + if err != nil { + return err + } + + return nil +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/README.md b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/README.md new file mode 100644 index 000000000..45c57ab69 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/README.md @@ -0,0 +1,22 @@ +# README + +## About + +This is a basic Svelte template, using rollup to bundle the assets into a single JS file. +Rollup is configured to do the following: + +- Convert imported images to base64 strings +- Convert `url()` in `@font-face` declarations to base64 strings +- Bundle all css into the JS bundle +- Copy `index.html` from `frontend/src/` to `frontend/dist/` + +Clicking the button will call the backend. + +## Live Development + +To run in live development mode, run `wails dev` in the project directory. The frontend dev server will run +on http://localhost:34115. Open this in your browser to connect to your application. + +## Building + +For a production build, use `wails build`. diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/app.go b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/app.go new file mode 100644 index 000000000..d03f9fc11 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/app.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + "fmt" +) + +// App application struct +type App struct { + ctx context.Context +} + +// NewApp creates a new App application struct +func NewApp() *App { + return &App{} +} + +// startup is called at application startup +func (b *App) startup(ctx context.Context) { + // Perform your setup here + b.ctx = ctx +} + +// domReady is called after the front-end dom has been loaded +func (b *App) domReady(ctx context.Context) { + // Add your action here +} + +// shutdown is called at application termination +func (b *App) shutdown(ctx context.Context) { + // Perform your teardown here +} + +// Greet returns a greeting for the given name +func (b *App) Greet(name string) string { + return fmt.Sprintf("Hello %s!", name) +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/.gitignore b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/.gitignore new file mode 100644 index 000000000..ba0af2a15 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/.gitignore @@ -0,0 +1,4 @@ +/node_modules/ +/dist/build/ + +.DS_Store diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/README.md b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/README.md new file mode 100644 index 000000000..7b1ba8363 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/README.md @@ -0,0 +1,105 @@ +*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)* + +--- + +# svelte app + +This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template. + +To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit): + +```bash +npx degit sveltejs/template svelte-app +cd svelte-app +``` + +*Note that you will need to have [Node.js](https://nodejs.org) installed.* + + +## Get started + +Install the dependencies... + +```bash +cd svelte-app +npm install +``` + +...then start [Rollup](https://rollupjs.org): + +```bash +npm run dev +``` + +Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes. + +By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`. + +If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense. + +## Building and running in production mode + +To create an optimised version of the app: + +```bash +npm run build +``` + +You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com). + + +## Single-page app mode + +By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere. + +If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json: + +```js +"start": "sirv public --single" +``` + +## Using TypeScript + +This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with: + +```bash +node scripts/setupTypeScript.js +``` + +Or remove the script via: + +```bash +rm scripts/setupTypeScript.js +``` + +## Deploying to the web + +### With [Vercel](https://vercel.com) + +Install `vercel` if you haven't already: + +```bash +npm install -g vercel +``` + +Then, from within your project folder: + +```bash +cd public +vercel deploy --name my-project +``` + +### With [surge](https://surge.sh/) + +Install `surge` if you haven't already: + +```bash +npm install -g surge +``` + +Then, from within your project folder: + +```bash +npm run build +surge public my-project.surge.sh +``` diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/dist/index.html b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/dist/index.html new file mode 100644 index 000000000..58f98f0d1 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/dist/index.html @@ -0,0 +1 @@ + diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/package-lock.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/package-lock.tmpl.json new file mode 100644 index 000000000..f5c7c84cc --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/package-lock.tmpl.json @@ -0,0 +1,3638 @@ +{ + "name": "{{.ProjectName}}", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@polka/url": { + "version": "1.0.0-next.15", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.15.tgz", + "integrity": "sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA==" + }, + "@rollup/plugin-commonjs": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", + "integrity": "sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + } + }, + "@rollup/plugin-image": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-image/-/plugin-image-2.0.6.tgz", + "integrity": "sha512-bB+spXogbPiFjhBS7i8ajUOgOnVwWK3bnJ6VroxKey/q8/EPRkoSh+4O1qPCw97qMIDspF4TlzXVBhZ7nojIPw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "mini-svg-data-uri": "^1.2.3" + } + }, + "@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + } + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/fs-extra": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", + "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==", + "dev": true + }, + "@types/node": { + "version": "15.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.5.tgz", + "integrity": "sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==", + "dev": true + }, + "@types/q": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", + "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", + "dev": true + }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", + "dev": true + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001241", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz", + "integrity": "sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "console-clear": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz", + "integrity": "sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "requires": { + "css-tree": "^1.1.2" + }, + "dependencies": { + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "electron-to-chromium": { + "version": "1.3.762", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.762.tgz", + "integrity": "sha512-LehWjRpfPcK8F1Lf/NZoAwWLWnjJVo0SZeQ9j/tvnBWYcT99qDqgo4raAfS2oTKZjPrR/jxruh85DGgDUmywEA==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "fast-glob": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.6.tgz", + "integrity": "sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "generic-names": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz", + "integrity": "sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0" + } + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", + "dev": true + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", + "dev": true, + "requires": { + "import-from": "^3.0.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "dev": true + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "lilconfig": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.3.tgz", + "integrity": "sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==", + "dev": true + }, + "livereload": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz", + "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "dev": true, + "requires": { + "chokidar": "^3.5.0", + "livereload-js": "^3.3.1", + "opts": ">= 1.2.0", + "ws": "^7.4.3" + } + }, + "livereload-js": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.2.tgz", + "integrity": "sha512-w677WnINxFkuixAoUEXOStewzLYGI76XVag+0JWMMEyjJQKs0ibWZMxkTlB96Lm3EjZ7IeOxVziBEbtxVQqQZA==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "local-access": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/local-access/-/local-access-1.1.0.tgz", + "integrity": "sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw==" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mime": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" + }, + "mini-svg-data-uri": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.3.3.tgz", + "integrity": "sha512-+fA2oRcR1dJI/7ITmeQJDrYWks0wodlOz0pAEhKYJ2IVc1z0AnwJUsKY2fzFmPAM3Jo9J0rBx8JAA9QQSJ5PuA==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mri": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", + "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==" + }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true + }, + "node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, + "object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opts": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true + }, + "postcss": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz", + "integrity": "sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + } + }, + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-load-config": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.0.tgz", + "integrity": "sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g==", + "dev": true, + "requires": { + "import-cwd": "^3.0.0", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/postcss-modules/-/postcss-modules-4.1.3.tgz", + "integrity": "sha512-dBT39hrXe4OAVYJe/2ZuIZ9BzYhOe7t+IhedYeQ2OxKwDpAGlkEN/fR0fGnrbx4BvgbMReRX4hCubYK9cE/pJQ==", + "dev": true, + "requires": { + "generic-names": "^2.0.1", + "icss-replace-symbols": "^1.1.0", + "lodash.camelcase": "^4.3.0", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "string-hash": "^1.1.1" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-url": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-10.1.3.tgz", + "integrity": "sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==", + "dev": true, + "requires": { + "make-dir": "~3.1.0", + "mime": "~2.5.2", + "minimatch": "~3.0.4", + "xxhashjs": "~0.2.2" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "promise.series": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/promise.series/-/promise.series-0.2.0.tgz", + "integrity": "sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", + "dev": true + }, + "rollup": { + "version": "2.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.52.4.tgz", + "integrity": "sha512-AXgUxxWXyGfsj8GKleR1k8KsG8G+7ZZDRU9RZb9PnLGSyTqI/1qf/+QSp1hRaR40j4yfBCKXs5khtGKiFwihfg==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-copy": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz", + "integrity": "sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==", + "dev": true, + "requires": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + } + }, + "rollup-plugin-css-only": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz", + "integrity": "sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA==", + "dev": true, + "requires": { + "@rollup/pluginutils": "4" + }, + "dependencies": { + "@rollup/pluginutils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.0.tgz", + "integrity": "sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==", + "dev": true, + "requires": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + } + } + } + }, + "rollup-plugin-livereload": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz", + "integrity": "sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==", + "dev": true, + "requires": { + "livereload": "^0.9.1" + } + }, + "rollup-plugin-postcss": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-4.0.0.tgz", + "integrity": "sha512-OQzT+YspV01/6dxfyEw8lBO2px3hyL8Xn+k2QGctL7V/Yx2Z1QaMKdYVslP1mqv7RsKt6DROIlnbpmgJ3yxf6g==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "concat-with-sourcemaps": "^1.1.0", + "cssnano": "^4.1.10", + "import-cwd": "^3.0.0", + "p-queue": "^6.6.2", + "pify": "^5.0.0", + "postcss-load-config": "^3.0.0", + "postcss-modules": "^4.0.0", + "promise.series": "^0.2.0", + "resolve": "^1.19.0", + "rollup-pluginutils": "^2.8.2", + "safe-identifier": "^0.4.2", + "style-inject": "^0.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "rollup-plugin-svelte": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz", + "integrity": "sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==", + "dev": true, + "requires": { + "require-relative": "^0.8.7", + "rollup-pluginutils": "^2.8.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + } + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "sade": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz", + "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==", + "requires": { + "mri": "^1.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safe-identifier": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", + "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "semiver": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", + "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "sirv": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.12.tgz", + "integrity": "sha512-+jQoCxndz7L2tqQL4ZyzfDhky0W/4ZJip3XoOuxyQWnAwMxindLl3Xv1qT4x1YX/re0leShvTm8Uk0kQspGhBg==", + "requires": { + "@polka/url": "^1.0.0-next.15", + "mime": "^2.3.1", + "totalist": "^1.0.0" + } + }, + "sirv-cli": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/sirv-cli/-/sirv-cli-1.0.12.tgz", + "integrity": "sha512-Rs5PvF3a48zuLmrl8vcqVv9xF/WWPES19QawVkpdzqx7vD5SMZS07+ece1gK4umbslXN43YeIksYtQM5csgIzQ==", + "requires": { + "console-clear": "^1.1.0", + "get-port": "^3.2.0", + "kleur": "^3.0.0", + "local-access": "^1.0.1", + "sade": "^1.6.0", + "semiver": "^1.0.0", + "sirv": "^1.0.12", + "tinydate": "^1.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=", + "dev": true + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "style-inject": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-inject/-/style-inject-0.3.0.tgz", + "integrity": "sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==", + "dev": true + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "postcss": { + "version": "7.0.36", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz", + "integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svelte": { + "version": "3.38.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.38.3.tgz", + "integrity": "sha512-N7bBZJH0iF24wsalFZF+fVYMUOigaAUQMIcEKHO3jstK/iL8VmP9xE+P0/a76+FkNcWt+TDv2Gx1taUoUscrvw==", + "dev": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "terser": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", + "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", + "dev": true + }, + "tinydate": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinydate/-/tinydate-1.3.0.tgz", + "integrity": "sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.1.tgz", + "integrity": "sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==", + "dev": true + }, + "xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", + "dev": true, + "requires": { + "cuint": "^0.2.2" + } + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } + } +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/package.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/package.tmpl.json new file mode 100644 index 000000000..a49448c62 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/package.tmpl.json @@ -0,0 +1,28 @@ +{ + "name": "{{.ProjectName}}", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "rollup -c", + "dev": "rollup -c -w", + "start": "sirv dist --no-clear" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-image": "^2.0.6", + "@rollup/plugin-node-resolve": "^11.0.0", + "postcss": "^8.3.5", + "postcss-url": "^10.1.3", + "rollup": "^2.3.4", + "rollup-plugin-copy": "^3.4.0", + "rollup-plugin-livereload": "^2.0.0", + "rollup-plugin-postcss": "^4.0.0", + "rollup-plugin-svelte": "^7.0.0", + "rollup-plugin-terser": "^7.0.0", + "svelte": "^3.0.0" + }, + "dependencies": { + "sirv-cli": "^1.0.0" + }, + "author": "{{.AuthorName}}" +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/rollup.config.js b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/rollup.config.js new file mode 100644 index 000000000..42685908c --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/rollup.config.js @@ -0,0 +1,83 @@ +import svelte from 'rollup-plugin-svelte'; +import commonjs from '@rollup/plugin-commonjs'; +import resolve from '@rollup/plugin-node-resolve'; +import livereload from 'rollup-plugin-livereload'; +import {terser} from 'rollup-plugin-terser'; +import copy from 'rollup-plugin-copy'; +import postcss from 'rollup-plugin-postcss' + +const production = !process.env.ROLLUP_WATCH; + +function serve() { + let server; + + function toExit() { + if (server) server.kill(0); + } + + return { + writeBundle() { + if (server) return; + server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { + stdio: ['ignore', 'inherit', 'inherit'], + shell: true + }); + + process.on('SIGTERM', toExit); + process.on('exit', toExit); + } + }; +} + +export default { + input: 'src/main.js', + output: { + sourcemap: true, + format: 'iife', + name: 'app', + file: 'dist/bundle.js' + }, + plugins: [ + svelte({ + compilerOptions: { + // enable run-time checks when not in production + dev: !production + } + }), + postcss({ + minimize: true, + }), + // If you have external dependencies installed from + // npm, you'll most likely need these plugins. In + // some cases you'll need additional configuration - + // consult the documentation for details: + // https://github.com/rollup/plugins/tree/master/packages/commonjs + resolve({ + browser: true, + dedupe: ['svelte'] + }), + commonjs(), + copy({ + targets: [ + {src: 'src/index.html', dest: 'dist/'}, + {src: 'src/global.css', dest: 'dist/'}, + {src: 'src/assets', dest: 'dist/'}, + ] + }), + + // In dev mode, call `npm run start` once + // the bundle has been generated + !production && serve(), + + // Watch the `dist` directory and refresh the + // browser on changes when not in production + !production && livereload('dist'), + + // If we're building for production (npm run build + // instead of npm run dev), minify + production && terser() + ], + watch: { + clearScreen: false + } +}; diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/scripts/setupTypeScript.js b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/scripts/setupTypeScript.js new file mode 100644 index 000000000..133658af1 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/scripts/setupTypeScript.js @@ -0,0 +1,121 @@ +// @ts-check + +/** This script modifies the project to support TS code in .svelte files like: + + + + As well as validating the code for CI. + */ + +/** To work on this script: + rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template +*/ + +const fs = require("fs") +const path = require("path") +const { argv } = require("process") + +const projectRoot = argv[2] || path.join(__dirname, "..") + +// Add deps to pkg.json +const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")) +packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { + "svelte-check": "^2.0.0", + "svelte-preprocess": "^4.0.0", + "@rollup/plugin-typescript": "^8.0.0", + "typescript": "^4.0.0", + "tslib": "^2.0.0", + "@tsconfig/svelte": "^2.0.0" +}) + +// Add script for checking +packageJSON.scripts = Object.assign(packageJSON.scripts, { + "check": "svelte-check --tsconfig ./tsconfig.json" +}) + +// Write the package JSON +fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " ")) + +// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too +const beforeMainJSPath = path.join(projectRoot, "src", "main.js") +const afterMainTSPath = path.join(projectRoot, "src", "main.ts") +fs.renameSync(beforeMainJSPath, afterMainTSPath) + +// Switch the app.svelte file to use TS +const appSveltePath = path.join(projectRoot, "src", "App.svelte") +let appFile = fs.readFileSync(appSveltePath, "utf8") +appFile = appFile.replace(" + +
+ +
+ + +
+ {#if greeting} +
{greeting}
+ {/if} +
+ + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/fonts/OFL.txt b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/fonts/OFL.txt new file mode 100644 index 000000000..bad763db1 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com), + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 new file mode 100644 index 000000000..2f9cc5964 Binary files /dev/null and b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 differ diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/images/logo-dark.svg b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/images/logo-dark.svg new file mode 100644 index 000000000..66fa483c4 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/assets/images/logo-dark.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/global.css b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/global.css new file mode 100644 index 000000000..3827664ca --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/global.css @@ -0,0 +1,25 @@ + +html { + text-align: center; + color: white; + background-color: rgba(0, 0, 0, 255); + width: 100%; + height: 100%; +} + +body { + color: white; + font-family: 'Nunito', -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + margin: 0; + width: 100%; + height: 100%; + overscroll-behavior: none; +} + +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 400; + src: local(''), + url('./assets/fonts/nunito-v16-latin-regular.woff2') format('woff2') +} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/index.tmpl.html b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/index.tmpl.html new file mode 100644 index 000000000..d6dfa7948 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/index.tmpl.html @@ -0,0 +1,14 @@ + + + + + + + {{.ProjectName}} + + + + + + + diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/main.js b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/main.js new file mode 100644 index 000000000..d80e9a350 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/frontend/src/main.js @@ -0,0 +1,7 @@ +import App from './App.svelte'; + +const app = new App({ + target: document.body, +}); + +export default app; \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/gitignore.txt b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/gitignore.txt new file mode 100644 index 000000000..da44b7c53 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/gitignore.txt @@ -0,0 +1,9 @@ +# Wails bin directory +build/bin + +# IDEs +.idea +.vscode + +# The black hole that is... +node_modules diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.mod.tmpl b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.mod.tmpl new file mode 100644 index 000000000..2c68d66ed --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.mod.tmpl @@ -0,0 +1,38 @@ +module changeme + +go 1.17 + +require github.com/wailsapp/wails/v2 {{.WailsVersion}} + +require ( +github.com/andybalholm/brotli v1.0.2 // indirect +github.com/davecgh/go-spew v1.1.1 // indirect +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect +github.com/gabriel-vasile/mimetype v1.3.1 // indirect +github.com/go-ole/go-ole v1.2.5 // indirect +github.com/gofiber/fiber/v2 v2.17.0 // indirect +github.com/gofiber/websocket/v2 v2.0.8 // indirect +github.com/google/uuid v1.1.2 // indirect +github.com/imdario/mergo v0.3.12 // indirect +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect +github.com/klauspost/compress v1.12.2 // indirect +github.com/leaanthony/debme v1.2.1 // indirect +github.com/leaanthony/go-ansi-parser v1.0.1 // indirect +github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect +github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect +github.com/leaanthony/slicer v1.5.0 // indirect +github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect +github.com/leaanthony/webview2runtime v1.1.0 // indirect +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect +github.com/pkg/errors v0.9.1 // indirect +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect +github.com/tkrajina/go-reflector v0.5.5 // indirect +github.com/valyala/bytebufferpool v1.0.0 // indirect +github.com/valyala/fasthttp v1.28.0 // indirect +github.com/valyala/tcplisten v1.0.0 // indirect +golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect +) + +// replace github.com/wailsapp/wails/v2 {{.WailsVersion}} => {{.WailsDirectory}} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.sum b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.sum new file mode 100644 index 000000000..575af8230 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/go.sum @@ -0,0 +1,228 @@ +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab h1:9e2joQGp642wHGFP5m86SDptAavrdGBe8/x9DGEEAaI= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flytam/filenamify v1.0.0/go.mod h1:Dzf9kVycwcsBlr2ATg6uxjqiFgKGH+5SKFuhdeP5zu8= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= +github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gofiber/fiber/v2 v2.17.0 h1:qP3PkGUbBB0i9iQh5E057XI1yO5CZigUxZhyUFYAFoM= +github.com/gofiber/fiber/v2 v2.17.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg= +github.com/gofiber/websocket/v2 v2.0.8 h1:Hb4y6IxYZVMO0segROODXJiXVgVD3a6i7wnfot8kM6k= +github.com/gofiber/websocket/v2 v2.0.8/go.mod h1:fv8HSGQX09sauNv9g5Xq8GeGAaahLFYQKKb4ZdT0x2w= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= +github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8= +github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= +github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= +github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4= +github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= +github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= +github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= +github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg= +github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM= +github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= +github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= +github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= +github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= +github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= +github.com/leaanthony/webview2runtime v1.1.0 h1:N0pv55ift8XtqozIp4PNOtRCJ/Qdd/qzx80lUpalS4c= +github.com/leaanthony/webview2runtime v1.1.0/go.mod h1:hH9GnWCve3DYzNaPOcPbhHQ7fodXR1QJNsnwixid4Tk= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs= +github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tc-hib/winres v0.1.5/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A= +github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= +github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs= +github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= +github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4= +github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/wailsapp/wails/v2 v2.0.0-beta.3 h1:8vhBbnjpYDF6cCUwKadon7J/98UjcP1nrnptUl70Tfg= +github.com/wailsapp/wails/v2 v2.0.0-beta.3/go.mod h1:aku28riyHF2G5jmx/qtxjLWi7VwpTjhhX/HVLCptWFA= +github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28= +github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/main.tmpl.go b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/main.tmpl.go new file mode 100644 index 000000000..89a4cecbb --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/main.tmpl.go @@ -0,0 +1,54 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/logger" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) + +//go:embed frontend/dist +var assets embed.FS + +func main() { + // Create an instance of the app structure + app := NewApp() + + // Create application with options + err := wails.Run(&options.App{ + Title: "{{.ProjectName}}", + Width: 720, + Height: 570, + MinWidth: 720, + MinHeight: 570, + MaxWidth: 1280, + MaxHeight: 740, + DisableResize: false, + Fullscreen: false, + Frameless: false, + StartHidden: false, + HideWindowOnClose: false, + RGBA: &options.RGBA{R: 255, G: 255, B: 255, A: 255}, + Assets: assets, + LogLevel: logger.DEBUG, + OnStartup: app.startup, + OnDomReady: app.domReady, + OnShutdown: app.shutdown, + Bind: []interface{}{ + app, + }, + // Windows platform specific options + Windows: &windows.Options{ + WebviewIsTransparent: false, + WindowIsTranslucent: false, + DisableWindowIcon: false, + }, + }) + + if err != nil { + log.Fatal(err) + } +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/template.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/template.json new file mode 100644 index 000000000..f04858241 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/template.json @@ -0,0 +1,7 @@ +{ + "name": "Basic Svelte + Rollup", + "shortname": "svelte", + "author": "Lea Anthony ", + "description": "Svelte template using rollup to bundle css, images and fonts", + "helpurl": "https://github.com/wailsapp/wails" +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/wails.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/wails.tmpl.json new file mode 100644 index 000000000..b8d08108d --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/svelte/wails.tmpl.json @@ -0,0 +1,11 @@ +{ + "name": "{{.ProjectName}}", + "outputfilename": "{{.BinaryName}}", + "assetdir": "frontend/dist", + "frontend:install": "npm install", + "frontend:build": "npm run build", + "author": { + "name": "{{.AuthorName}}", + "email": "{{.AuthorEmail}}" + } +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/README.md b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/README.md new file mode 100644 index 000000000..5b2db9baf --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/README.md @@ -0,0 +1,15 @@ +# README + +## About + +This template uses vanilla JS / HTML and CSS. + +## Live Development + +To run in live development mode, run `wails dev` in the project directory. The frontend dev server will run +on http://localhost:34115. Open this in your browser to connect to your application. + +## Building + +For a production build, use `wails build`. + diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/app.go b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/app.go new file mode 100644 index 000000000..d04d123d4 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/app.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + "fmt" +) + +// App struct +type App struct { + ctx context.Context +} + +// NewApp creates a new App application struct +func NewApp() *App { + return &App{} +} + +// startup is called at application startup +func (b *App) startup(ctx context.Context) { + // Perform your setup here + b.ctx = ctx +} + +// domReady is called after the front-end dom has been loaded +func (b *App) domReady(ctx context.Context) { + // Add your action here +} + +// shutdown is called at application termination +func (b *App) shutdown(ctx context.Context) { + // Perform your teardown here +} + +// Greet returns a greeting for the given name +func (b *App) Greet(name string) string { + return fmt.Sprintf("Hello %s, It's show time!", name) +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/fonts/OFL.txt b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/fonts/OFL.txt new file mode 100644 index 000000000..9cac04ce8 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com), + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 new file mode 100644 index 000000000..2f9cc5964 Binary files /dev/null and b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 differ diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/images/logo-dark.svg b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/images/logo-dark.svg new file mode 100644 index 000000000..855d7e0ef --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/assets/images/logo-dark.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/index.tmpl.html b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/index.tmpl.html new file mode 100644 index 000000000..73eebf442 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/index.tmpl.html @@ -0,0 +1,19 @@ + + + + {{.ProjectName}} + + + + + +
Please enter your name below 👇
+
+ + +
+ + + + + \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/main.css b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/main.css new file mode 100644 index 000000000..1a73a0da8 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/main.css @@ -0,0 +1,73 @@ +html { + background-color: rgba(33, 37, 43, 1); + text-align: center; + color: white; +} + +body { + margin: 0; + color: white; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", + "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + overscroll-behavior: none; +} + +@font-face { + font-family: "Nunito"; + font-style: normal; + font-weight: 400; + src: local(""), + url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2"); +} + +.logo { + display: block; + width: 35%; + height: 35%; + margin: auto; + padding: 15% 0 0; + background-position: center; + background-repeat: no-repeat; + background-image: url("./assets/images/logo-dark.svg"); +} +.result { + height: 20px; + line-height: 20px; + margin: 1.5rem auto; +} +.input-box .btn { + width: 60px; + height: 30px; + line-height: 30px; + border-radius: 3px; + border: none; + margin: 0 0 0 20px; + padding: 0 8px; + cursor: pointer; +} +.input-box .btn:hover { + background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%); + color: #333333; +} + +.input-box .input { + border: none; + border-radius: 3px; + outline: none; + height: 30px; + line-height: 30px; + padding: 0 10px; + background-color: rgba(240, 240, 240, 1); + -webkit-font-smoothing: antialiased; +} + +.input-box .input:hover { + border: none; + background-color: rgba(255, 255, 255, 1); +} + +.input-box .input:focus { + border: none; + background-color: rgba(255, 255, 255, 1); +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/main.js b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/main.js new file mode 100644 index 000000000..db404e459 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/frontend/src/main.js @@ -0,0 +1,23 @@ +// Get input + focus +let nameElement = document.getElementById("name"); +nameElement.focus(); + +// Setup the greet function +window.greet = function () { + + // Get name + let name = nameElement.value; + + // Call App.Greet(name) + window.go.main.App.Greet(name).then((result) => { + // Update result with data back from App.Greet() + document.getElementById("result").innerText = result; + }); +}; + +nameElement.onkeydown = function (e) { + console.log(e) + if (e.keyCode == 13) { + window.greet() + } +} \ No newline at end of file diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/gitignore.txt b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/gitignore.txt new file mode 100644 index 000000000..da44b7c53 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/gitignore.txt @@ -0,0 +1,9 @@ +# Wails bin directory +build/bin + +# IDEs +.idea +.vscode + +# The black hole that is... +node_modules diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.mod.tmpl b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.mod.tmpl new file mode 100644 index 000000000..2c68d66ed --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.mod.tmpl @@ -0,0 +1,38 @@ +module changeme + +go 1.17 + +require github.com/wailsapp/wails/v2 {{.WailsVersion}} + +require ( +github.com/andybalholm/brotli v1.0.2 // indirect +github.com/davecgh/go-spew v1.1.1 // indirect +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect +github.com/gabriel-vasile/mimetype v1.3.1 // indirect +github.com/go-ole/go-ole v1.2.5 // indirect +github.com/gofiber/fiber/v2 v2.17.0 // indirect +github.com/gofiber/websocket/v2 v2.0.8 // indirect +github.com/google/uuid v1.1.2 // indirect +github.com/imdario/mergo v0.3.12 // indirect +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect +github.com/klauspost/compress v1.12.2 // indirect +github.com/leaanthony/debme v1.2.1 // indirect +github.com/leaanthony/go-ansi-parser v1.0.1 // indirect +github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect +github.com/leaanthony/go-webview2 v0.0.0-20210914103035-f00aa774a934 // indirect +github.com/leaanthony/slicer v1.5.0 // indirect +github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect +github.com/leaanthony/webview2runtime v1.1.0 // indirect +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect +github.com/pkg/errors v0.9.1 // indirect +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect +github.com/tkrajina/go-reflector v0.5.5 // indirect +github.com/valyala/bytebufferpool v1.0.0 // indirect +github.com/valyala/fasthttp v1.28.0 // indirect +github.com/valyala/tcplisten v1.0.0 // indirect +golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect +) + +// replace github.com/wailsapp/wails/v2 {{.WailsVersion}} => {{.WailsDirectory}} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.sum b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.sum new file mode 100644 index 000000000..575af8230 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/go.sum @@ -0,0 +1,228 @@ +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab h1:9e2joQGp642wHGFP5m86SDptAavrdGBe8/x9DGEEAaI= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flytam/filenamify v1.0.0/go.mod h1:Dzf9kVycwcsBlr2ATg6uxjqiFgKGH+5SKFuhdeP5zu8= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= +github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gofiber/fiber/v2 v2.17.0 h1:qP3PkGUbBB0i9iQh5E057XI1yO5CZigUxZhyUFYAFoM= +github.com/gofiber/fiber/v2 v2.17.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg= +github.com/gofiber/websocket/v2 v2.0.8 h1:Hb4y6IxYZVMO0segROODXJiXVgVD3a6i7wnfot8kM6k= +github.com/gofiber/websocket/v2 v2.0.8/go.mod h1:fv8HSGQX09sauNv9g5Xq8GeGAaahLFYQKKb4ZdT0x2w= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= +github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8= +github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= +github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= +github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4= +github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= +github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= +github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= +github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd h1:6m4zZ/esiByaDbzgdvDxjsOaIDgtuG1q2cyhjAi6uAg= +github.com/leaanthony/go-webview2 v0.0.0-20210928094513-a94a08b538bd/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM= +github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= +github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= +github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= +github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= +github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= +github.com/leaanthony/webview2runtime v1.1.0 h1:N0pv55ift8XtqozIp4PNOtRCJ/Qdd/qzx80lUpalS4c= +github.com/leaanthony/webview2runtime v1.1.0/go.mod h1:hH9GnWCve3DYzNaPOcPbhHQ7fodXR1QJNsnwixid4Tk= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs= +github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tc-hib/winres v0.1.5/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A= +github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= +github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs= +github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= +github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4= +github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/wailsapp/wails/v2 v2.0.0-beta.3 h1:8vhBbnjpYDF6cCUwKadon7J/98UjcP1nrnptUl70Tfg= +github.com/wailsapp/wails/v2 v2.0.0-beta.3/go.mod h1:aku28riyHF2G5jmx/qtxjLWi7VwpTjhhX/HVLCptWFA= +github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28= +github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/main.tmpl.go b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/main.tmpl.go new file mode 100644 index 000000000..28d7826d9 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/main.tmpl.go @@ -0,0 +1,54 @@ +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/logger" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) + +//go:embed frontend/src +var assets embed.FS + +func main() { + // Create an instance of the app structure + app := NewApp() + + // Create application with options + err := wails.Run(&options.App{ + Title: "{{.ProjectName}}", + Width: 720, + Height: 570, + MinWidth: 720, + MinHeight: 570, + MaxWidth: 1280, + MaxHeight: 740, + DisableResize: false, + Fullscreen: false, + Frameless: false, + StartHidden: false, + HideWindowOnClose: false, + RGBA: &options.RGBA{R: 255, G: 255, B: 255, A: 255}, + Assets: assets, + LogLevel: logger.DEBUG, + OnStartup: app.startup, + OnDomReady: app.domReady, + OnShutdown: app.shutdown, + Bind: []interface{}{ + app, + }, + // Windows platform specific options + Windows: &windows.Options{ + WebviewIsTransparent: false, + WindowIsTranslucent: false, + DisableWindowIcon: false, + }, + }) + + if err != nil { + log.Fatal(err) + } +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/template.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/template.json new file mode 100644 index 000000000..f235e50e7 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/template.json @@ -0,0 +1,7 @@ +{ + "name": "Vanilla HTML/JS/CSS", + "shortname": "vanilla", + "author": "Lea Anthony ", + "description": "A simple template using only HTML/CSS/JS", + "helpurl": "https://github.com/wailsapp/wails" +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/wails.tmpl.json b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/wails.tmpl.json new file mode 100644 index 000000000..0f6a23103 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vanilla/wails.tmpl.json @@ -0,0 +1,9 @@ +{ + "name": "{{.ProjectName}}", + "outputfilename": "{{.BinaryName}}", + "assetdir": "frontend/src", + "author": { + "name": "{{.AuthorName}}", + "email": "{{.AuthorEmail}}" + } +} diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates_test.go b/v2/cmd/wails/internal/commands/initialise/templates/templates_test.go new file mode 100644 index 000000000..dcff8d6e8 --- /dev/null +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates_test.go @@ -0,0 +1,46 @@ +package templates + +import ( + "fmt" + "testing" + + "github.com/matryer/is" +) + +func TestList(t *testing.T) { + + is2 := is.New(t) + templates, err := List() + is2.NoErr(err) + + println("Found these templates:") + for _, template := range templates { + fmt.Printf("%+v\n", template) + } +} + +func TestShortname(t *testing.T) { + + is2 := is.New(t) + + template, err := getTemplateByShortname("vanilla") + is2.NoErr(err) + + println("Found this template:") + fmt.Printf("%+v\n", template) +} + +func TestInstall(t *testing.T) { + + is2 := is.New(t) + + options := &Options{ + ProjectName: "test", + TemplateName: "vanilla", + AuthorName: "Lea Anthony", + AuthorEmail: "lea.anthony@gmail.com", + } + + _, _, err := Install(options) + is2.NoErr(err) +} diff --git a/v2/cmd/wails/internal/commands/update/update.go b/v2/cmd/wails/internal/commands/update/update.go new file mode 100644 index 000000000..5eef6af25 --- /dev/null +++ b/v2/cmd/wails/internal/commands/update/update.go @@ -0,0 +1,166 @@ +package update + +import ( + "fmt" + "io" + "log" + "os" + + "github.com/wailsapp/wails/v2/internal/shell" + + "github.com/wailsapp/wails/v2/internal/github" + + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/pkg/clilogger" +) + +// AddSubcommand adds the `init` command for the Wails application +func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error { + + command := app.NewSubCommand("update", "Update the Wails CLI") + command.LongDescription(`This command allows you to update your version of the Wails CLI.`) + + // Setup flags + var prereleaseRequired bool + command.BoolFlag("pre", "Update CLI to latest Prerelease", &prereleaseRequired) + + var specificVersion string + command.StringFlag("version", "Install a specific version (Overrides other flags) of the CLI", &specificVersion) + + command.Action(func() error { + + // Create logger + logger := clilogger.New(w) + + // Print banner + app.PrintBanner() + logger.Println("Checking for updates...") + + var desiredVersion *github.SemanticVersion + var err error + var valid bool + + if len(specificVersion) > 0 { + // Check if this is a valid version + valid, err = github.IsValidTag(specificVersion) + if err == nil { + if !valid { + err = fmt.Errorf("version '%s' is invalid", specificVersion) + } else { + desiredVersion, err = github.NewSemanticVersion(specificVersion) + } + } + } else { + if prereleaseRequired { + desiredVersion, err = github.GetLatestPreRelease() + } else { + desiredVersion, err = github.GetLatestStableRelease() + } + } + if err != nil { + return err + } + fmt.Println() + + fmt.Println(" Current Version : " + currentVersion) + + if len(specificVersion) > 0 { + fmt.Printf(" Desired Version : v%s\n", desiredVersion) + } else { + if prereleaseRequired { + fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion) + } else { + fmt.Printf(" Latest Release : v%s\n", desiredVersion) + } + } + + return updateToVersion(logger, desiredVersion, len(specificVersion) > 0, currentVersion) + }) + + return nil +} + +func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.SemanticVersion, force bool, currentVersion string) error { + + var targetVersionString = "v" + targetVersion.String() + + // Early exit + if targetVersionString == currentVersion { + logger.Println("\nLooks like you're up to date!") + return nil + } + + var desiredVersion string + + if !force { + + compareVersion := currentVersion + + currentVersion, err := github.NewSemanticVersion(compareVersion) + if err != nil { + return err + } + + var success bool + + // Release -> Pre-Release = Massage current version to prerelease format + if targetVersion.IsPreRelease() && currentVersion.IsRelease() { + testVersion, err := github.NewSemanticVersion(compareVersion + "-0") + if err != nil { + return err + } + success, _ = targetVersion.IsGreaterThan(testVersion) + } + // Pre-Release -> Release = Massage target version to prerelease format + if targetVersion.IsRelease() && currentVersion.IsPreRelease() { + // We are ok with greater than or equal + mainversion := currentVersion.MainVersion() + targetVersion, err = github.NewSemanticVersion(targetVersion.String()) + if err != nil { + return err + } + success, _ = targetVersion.IsGreaterThanOrEqual(mainversion) + } + + // Release -> Release = Standard check + if (targetVersion.IsRelease() && currentVersion.IsRelease()) || + (targetVersion.IsPreRelease() && currentVersion.IsPreRelease()) { + + success, _ = targetVersion.IsGreaterThan(currentVersion) + } + + // Compare + if !success { + logger.Println("Error: The requested version is lower than the current version.") + logger.Println("If this is what you really want to do, use `wails update -version %s`", targetVersionString) + return nil + } + + desiredVersion = "v" + targetVersion.String() + + } else { + desiredVersion = "v" + targetVersion.String() + } + + fmt.Println() + logger.Print("Installing Wails CLI " + desiredVersion + "...") + + // Run command in non module directory + homeDir, err := os.UserHomeDir() + if err != nil { + log.Fatal("Cannot find home directory! Please file a bug report!") + } + + sout, serr, err := shell.RunCommand(homeDir, "go", "install", "github.com/wailsapp/wails/v2/cmd/wails@"+desiredVersion) + if err != nil { + logger.Println("Failed.") + logger.Println(sout + `\n` + serr) + return err + } + logger.Println("\n") + logger.Println("Wails CLI updated to " + desiredVersion) + logger.Println("Make sure you update your project go.mod file to use " + desiredVersion + ":") + logger.Println(" require github.com/wailsapp/wails/v2 " + desiredVersion) + + return nil +} diff --git a/v2/cmd/wails/internal/tags.go b/v2/cmd/wails/internal/tags.go new file mode 100644 index 000000000..42743028d --- /dev/null +++ b/v2/cmd/wails/internal/tags.go @@ -0,0 +1,15 @@ +package internal + +import "strings" + +// ParseUserTags takes the string form of tags and converts to a slice of strings +func ParseUserTags(tagString string) []string { + userTags := make([]string, 0) + for _, tag := range strings.Split(tagString, " ") { + thisTag := strings.TrimSpace(tag) + if thisTag != "" { + userTags = append(userTags, thisTag) + } + } + return userTags +} diff --git a/v2/cmd/wails/internal/version.go b/v2/cmd/wails/internal/version.go new file mode 100644 index 000000000..4e6e434db --- /dev/null +++ b/v2/cmd/wails/internal/version.go @@ -0,0 +1,3 @@ +package internal + +var Version = "v2.0.0-beta.15" diff --git a/v2/cmd/wails/main.go b/v2/cmd/wails/main.go new file mode 100644 index 000000000..e69db18d7 --- /dev/null +++ b/v2/cmd/wails/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "fmt" + "github.com/wailsapp/wails/v2/cmd/wails/internal" + "os" + + "github.com/wailsapp/wails/v2/internal/colour" + + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update" + + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build" + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev" + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor" + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate" + "github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise" +) + +func fatal(message string) { + println(message) + os.Exit(1) +} + +func banner(_ *clir.Cli) string { + return fmt.Sprintf("%s %s\n", + colour.Yellow("Wails CLI"), + colour.DarkRed(internal.Version)) +} + +func printFooter() { + println(colour.Yellow("\nIf Wails is useful to you or your company, please consider sponsoring the project:\nhttps://github.com/sponsors/leaanthony\n")) +} + +func main() { + + var err error + + app := clir.NewCli("Wails", "Go/HTML Appkit", internal.Version) + + app.SetBannerFunction(banner) + defer printFooter() + + build.AddBuildSubcommand(app, os.Stdout) + err = initialise.AddSubcommand(app, os.Stdout) + if err != nil { + fatal(err.Error()) + } + + err = doctor.AddSubcommand(app, os.Stdout) + if err != nil { + fatal(err.Error()) + } + + err = dev.AddSubcommand(app, os.Stdout) + if err != nil { + fatal(err.Error()) + } + + err = generate.AddSubcommand(app, os.Stdout) + if err != nil { + fatal(err.Error()) + } + + err = update.AddSubcommand(app, os.Stdout, internal.Version) + if err != nil { + fatal(err.Error()) + } + + command := app.NewSubCommand("version", "The Wails CLI version") + command.Action(func() error { + println(internal.Version) + return nil + }) + + err = app.Run() + if err != nil { + println("\n\nERROR: " + err.Error()) + printFooter() + os.Exit(1) + } +} diff --git a/v2/go.mod b/v2/go.mod new file mode 100644 index 000000000..d0058db4b --- /dev/null +++ b/v2/go.mod @@ -0,0 +1,82 @@ +module github.com/wailsapp/wails/v2 + +go 1.17 + +require ( + github.com/Masterminds/semver v1.5.0 + github.com/fatih/structtag v1.2.0 + github.com/flytam/filenamify v1.0.0 + github.com/fsnotify/fsnotify v1.4.9 + github.com/gabriel-vasile/mimetype v1.3.1 + github.com/go-git/go-billy/v5 v5.2.0 // indirect + github.com/go-git/go-git/v5 v5.3.0 + github.com/gofiber/fiber/v2 v2.17.0 + github.com/gofiber/websocket/v2 v2.0.8 + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/gorilla/websocket v1.4.1 + github.com/imdario/mergo v0.3.12 + github.com/jackmordaunt/icns v1.0.0 + github.com/leaanthony/clir v1.0.4 + github.com/leaanthony/debme v1.2.1 + github.com/leaanthony/go-ansi-parser v1.0.1 + github.com/leaanthony/go-common-file-dialog v1.0.3 + github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3 + github.com/leaanthony/gosod v1.0.3 + github.com/leaanthony/idgen v1.0.0 + github.com/leaanthony/slicer v1.5.0 + github.com/leaanthony/typescriptify-golang-structs v0.1.7 + github.com/leaanthony/webview2runtime v1.1.0 + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 + github.com/leaanthony/winicon v1.0.0 + github.com/matryer/is v1.4.0 + github.com/olekukonko/tablewriter v0.0.4 + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 + github.com/pkg/errors v0.9.1 + github.com/tc-hib/winres v0.1.5 + github.com/tdewolff/minify v2.3.6+incompatible + github.com/tdewolff/parse v2.3.4+incompatible // indirect + github.com/tdewolff/test v1.0.6 // indirect + github.com/tidwall/sjson v1.1.7 + github.com/wzshiming/ctc v1.2.3 + github.com/xyproto/xpm v1.2.1 + github.com/ztrue/tracerr v0.3.0 + golang.org/x/mod v0.4.1 + golang.org/x/net v0.0.0-20210510120150-4163338589ed + golang.org/x/sys v0.0.0-20211020174200-9d6173849985 + golang.org/x/tools v0.1.0 + nhooyr.io/websocket v1.8.6 +) + +require ( + github.com/Microsoft/go-winio v0.4.16 // indirect + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/emirpasic/gods v1.12.0 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/google/go-cmp v0.5.5 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e // indirect + github.com/mattn/go-runewidth v0.0.7 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/tidwall/gjson v1.8.0 // indirect + github.com/tidwall/match v1.0.3 // indirect + github.com/tidwall/pretty v1.1.0 // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae // indirect + github.com/xanzy/ssh-agent v0.3.0 // indirect + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect + golang.org/x/image v0.0.0-20201208152932-35266b937fa6 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect +) diff --git a/v2/go.sum b/v2/go.sum new file mode 100644 index 000000000..592545177 --- /dev/null +++ b/v2/go.sum @@ -0,0 +1,297 @@ +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab h1:9e2joQGp642wHGFP5m86SDptAavrdGBe8/x9DGEEAaI= +github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flytam/filenamify v1.0.0 h1:ewx6BY2dj7U6h2zGPJmt33q/BjkSf/YsY/woQvnUNIs= +github.com/flytam/filenamify v1.0.0/go.mod h1:Dzf9kVycwcsBlr2ATg6uxjqiFgKGH+5SKFuhdeP5zu8= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gabriel-vasile/mimetype v1.3.1 h1:qevA6c2MtE1RorlScnixeG0VA1H4xrXyhyX3oWBynNQ= +github.com/gabriel-vasile/mimetype v1.3.1/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.2.0 h1:GcoouCP9J+5slw2uXAocL70z8ml4A8B/H8nEPt6CLPk= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M= +github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc= +github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gofiber/fiber/v2 v2.17.0 h1:qP3PkGUbBB0i9iQh5E057XI1yO5CZigUxZhyUFYAFoM= +github.com/gofiber/fiber/v2 v2.17.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg= +github.com/gofiber/websocket/v2 v2.0.8 h1:Hb4y6IxYZVMO0segROODXJiXVgVD3a6i7wnfot8kM6k= +github.com/gofiber/websocket/v2 v2.0.8/go.mod h1:fv8HSGQX09sauNv9g5Xq8GeGAaahLFYQKKb4ZdT0x2w= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ= +github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= +github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8= +github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU= +github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc= +github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA= +github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4= +github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM= +github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y= +github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0= +github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3 h1:qhgrg3MhFRAIvtaqoqI+SrT+0wDYpxDMp9e3cvcxMpI= +github.com/leaanthony/go-webview2 v0.0.0-20211022194343-1e4c8d4226f3/go.mod h1:lS5ds4bruPk9d7lzdF/OH31Z0YCerI6MmHNFGsWoUnM= +github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ= +github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4= +github.com/leaanthony/idgen v1.0.0 h1:IZreR+JGEzFV4yeVuBZA25gM0keUoFy+RDUldncQ+Jw= +github.com/leaanthony/idgen v1.0.0/go.mod h1:4nBZnt8ml/f/ic/EVQuLxuj817RccT2fyrUaZFxrcVA= +github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY= +github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leaanthony/typescriptify-golang-structs v0.1.7 h1:yoznzWzyxkO/iWdlpq+aPcuJ5Y/hpjq/lmgMFmpjwl0= +github.com/leaanthony/typescriptify-golang-structs v0.1.7/go.mod h1:cWtOkiVhMF77e6phAXUcfNwYmMwCJ67Sij24lfvi9Js= +github.com/leaanthony/webview2runtime v1.1.0 h1:N0pv55ift8XtqozIp4PNOtRCJ/Qdd/qzx80lUpalS4c= +github.com/leaanthony/webview2runtime v1.1.0/go.mod h1:hH9GnWCve3DYzNaPOcPbhHQ7fodXR1QJNsnwixid4Tk= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 h1:5iOd93PZbpH4Iir8QkC4coFD+zEQEZSIRcjwjTFZkr0= +github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18/go.mod h1:KEbMsKoznsebyGHwLk5LqkFOxL5uXSRdvpP4+avmAMs= +github.com/leaanthony/winicon v1.0.0 h1:ZNt5U5dY71oEoKZ97UVwJRT4e+5xo5o/ieKuHuk8NqQ= +github.com/leaanthony/winicon v1.0.0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc= +github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY= +github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tc-hib/winres v0.1.5 h1:2dA5yfjdoEA3UyRaOC92HNMt3jap66pLzoW4MjpC/0M= +github.com/tc-hib/winres v0.1.5/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A= +github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo= +github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= +github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38= +github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= +github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ= +github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/sjson v1.1.7 h1:sgVPwu/yygHJ2m1pJDLgGM/h+1F5odx5Q9ljG3imRm8= +github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs= +github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ= +github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4= +github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c= +github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28= +github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae h1:tpXvBXC3hpQBDCc9OojJZCQMVRAbT3TTdUMP8WguXkY= +github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20= +github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg= +github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/ztrue/tracerr v0.3.0 h1:lDi6EgEYhPYPnKcjsYzmWw4EkFEoA/gfe+I9Y5f+h6Y= +github.com/ztrue/tracerr v0.3.0/go.mod h1:qEalzze4VN9O8tnhBXScfCrmoJo10o8TN5ciKjm6Mww= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211020174200-9d6173849985 h1:LOlKVhfDyahgmqa97awczplwkjzNaELFg3zRIJ13RYo= +golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/v2/init.go b/v2/init.go new file mode 100644 index 000000000..50dc23fdb --- /dev/null +++ b/v2/init.go @@ -0,0 +1,7 @@ +// +build !windows + +package wails + +func Init() error { + return nil +} diff --git a/v2/init_windows.go b/v2/init_windows.go new file mode 100644 index 000000000..173da0a89 --- /dev/null +++ b/v2/init_windows.go @@ -0,0 +1,15 @@ +package wails + +import ( + "fmt" + "syscall" +) + +// Init is called at the start of the application +func Init() error { + status, r, err := syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call() + if status == 0 { + return fmt.Errorf("exit status %d: %v %v", status, r, err) + } + return nil +} diff --git a/v2/internal/app/debug.go b/v2/internal/app/debug.go new file mode 100644 index 000000000..3a6a6916f --- /dev/null +++ b/v2/internal/app/debug.go @@ -0,0 +1,37 @@ +//go:build dev +// +build dev + +package app + +import ( + "flag" + "strings" + + "github.com/wailsapp/wails/v2/pkg/logger" +) + +// Init initialises the application for a debug environment +func (a *App) Init() error { + // Indicate debug mode + a.debug = true + + // Set log levels + loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error") + flag.Parse() + if len(*loglevel) > 0 { + switch strings.ToLower(*loglevel) { + case "trace": + a.logger.SetLogLevel(logger.TRACE) + case "info": + a.logger.SetLogLevel(logger.INFO) + case "warning": + a.logger.SetLogLevel(logger.WARNING) + case "error": + a.logger.SetLogLevel(logger.ERROR) + default: + a.logger.SetLogLevel(logger.DEBUG) + } + } + + return nil +} diff --git a/v2/internal/app/default.go b/v2/internal/app/default.go new file mode 100644 index 000000000..380c251be --- /dev/null +++ b/v2/internal/app/default.go @@ -0,0 +1,41 @@ +// +build !desktop,!hybrid,!server,!dev + +package app + +// This is the default application that will get run if the user compiles using `go build`. +// The reason we want to prevent that is that the `wails build` command does a lot of behind +// the scenes work such as asset compilation. If we allow `go build`, the state of these assets +// will be unknown and the application will not work as expected. + +import ( + "os" + + "github.com/wailsapp/wails/v2/internal/logger" + + "github.com/wailsapp/wails/v2/pkg/options" +) + +// App defines a Wails application structure +type App struct { + Title string + Width int + Height int + Resizable bool + + // Indicates if the app is running in debug mode + debug bool + + logger *logger.Logger +} + +// CreateApp returns a null application +func CreateApp(_ *options.App) (*App, error) { + return &App{}, nil +} + +// Run the application +func (a *App) Run() error { + println(`FATAL: This application was built using "go build". This is unsupported. Please compile using "wails build".`) + os.Exit(1) + return nil +} diff --git a/v2/internal/app/desktop.go b/v2/internal/app/desktop.go new file mode 100644 index 000000000..b27f92919 --- /dev/null +++ b/v2/internal/app/desktop.go @@ -0,0 +1,256 @@ +//go:build desktop && !server +// +build desktop,!server + +package app + +import ( + "context" + "sync" + + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/ffenestri" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/menumanager" + "github.com/wailsapp/wails/v2/internal/messagedispatcher" + "github.com/wailsapp/wails/v2/internal/servicebus" + "github.com/wailsapp/wails/v2/internal/signal" + "github.com/wailsapp/wails/v2/internal/subsystem" + "github.com/wailsapp/wails/v2/pkg/options" +) + +// App defines a Wails application structure +type App struct { + appType string + + window *ffenestri.Application + servicebus *servicebus.ServiceBus + logger *logger.Logger + signal *signal.Manager + options *options.App + + // Subsystems + log *subsystem.Log + runtime *subsystem.Runtime + event *subsystem.Event + //binding *subsystem.Binding + call *subsystem.Call + menu *subsystem.Menu + url *subsystem.URL + dispatcher *messagedispatcher.Dispatcher + + menuManager *menumanager.Manager + + // Indicates if the app is in debug mode + debug bool + + // This is our binding DB + bindings *binding.Bindings + + // OnStartup/OnShutdown + startupCallback func(ctx context.Context) + shutdownCallback func() +} + +// Create App +func CreateApp(appoptions *options.App) (*App, error) { + + // Merge default options + options.MergeDefaults(appoptions) + + // Set up logger + myLogger := logger.New(appoptions.Logger) + myLogger.SetLogLevel(appoptions.LogLevel) + + // Create the menu manager + menuManager := menumanager.NewManager() + + // Process the application menu + appMenu := options.GetApplicationMenu(appoptions) + menuManager.SetApplicationMenu(appMenu) + + // Process context menus + contextMenus := options.GetContextMenus(appoptions) + for _, contextMenu := range contextMenus { + menuManager.AddContextMenu(contextMenu) + } + + // Process tray menus + trayMenus := options.GetTrayMenus(appoptions) + for _, trayMenu := range trayMenus { + menuManager.AddTrayMenu(trayMenu) + } + + window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager) + + // Create binding exemptions - Ugly hack. There must be a better way + bindingExemptions := []interface{}{appoptions.OnStartup, appoptions.OnShutdown, appoptions.OnDomReady} + + result := &App{ + appType: "desktop", + window: window, + servicebus: servicebus.New(myLogger), + logger: myLogger, + bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions), + menuManager: menuManager, + startupCallback: appoptions.OnStartup, + shutdownCallback: appoptions.OnShutdown, + } + + result.options = appoptions + + // Initialise the app + err := result.Init() + if err != nil { + return nil, err + } + + // Preflight Checks + err = result.PreflightChecks(appoptions) + if err != nil { + return nil, err + } + + return result, nil + +} + +// Run the application +func (a *App) Run() error { + + var err error + + // Setup a context + var subsystemWaitGroup sync.WaitGroup + parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup) + ctx, cancel := context.WithCancel(parentContext) + + // Start the service bus + a.servicebus.Debug() + err = a.servicebus.Start() + if err != nil { + return err + } + + runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback) + if err != nil { + return err + } + a.runtime = runtimesubsystem + err = a.runtime.Start() + if err != nil { + return err + } + + // Start the logging subsystem + log, err := subsystem.NewLog(a.servicebus, a.logger) + if err != nil { + return err + } + a.log = log + err = a.log.Start() + if err != nil { + return err + } + + // create the dispatcher + dispatcher, err := messagedispatcher.New(a.servicebus, a.logger) + if err != nil { + return err + } + a.dispatcher = dispatcher + err = dispatcher.Start() + if err != nil { + return err + } + + if a.options.Mac.URLHandlers != nil { + // Start the url handler subsystem + url, err := subsystem.NewURL(a.servicebus, a.logger, a.options.Mac.URLHandlers) + if err != nil { + return err + } + a.url = url + err = a.url.Start() + if err != nil { + return err + } + } + + // Start the eventing subsystem + eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger) + if err != nil { + return err + } + a.event = eventsubsystem + err = a.event.Start() + if err != nil { + return err + } + + // Start the menu subsystem + menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager) + if err != nil { + return err + } + a.menu = menusubsystem + err = a.menu.Start() + if err != nil { + return err + } + + // Start the call subsystem + callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB()) + if err != nil { + return err + } + a.call = callSubsystem + err = a.call.Start() + if err != nil { + return err + } + + // Dump bindings as a debug + bindingDump, err := a.bindings.ToJSON() + if err != nil { + return err + } + + // Setup signal handler + signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger) + if err != nil { + return err + } + a.signal = signalsubsystem + a.signal.Start() + + err = a.window.Run(dispatcher, bindingDump, a.debug) + a.logger.Trace("Ffenestri.Run() exited") + if err != nil { + return err + } + + // Close down all the subsystems + a.logger.Trace("Cancelling subsystems") + cancel() + subsystemWaitGroup.Wait() + + a.logger.Trace("Cancelling dispatcher") + dispatcher.Close() + + // Close log + a.logger.Trace("Stopping log") + log.Close() + + a.logger.Trace("Stopping Service bus") + err = a.servicebus.Stop() + if err != nil { + return err + } + + // OnShutdown callback + if a.shutdownCallback != nil { + a.shutdownCallback() + } + + return nil +} diff --git a/v2/internal/app/dev.go b/v2/internal/app/dev.go new file mode 100644 index 000000000..3b7be059f --- /dev/null +++ b/v2/internal/app/dev.go @@ -0,0 +1,249 @@ +//go:build dev +// +build dev + +package app + +/* +import ( + "context" + "sync" + + "github.com/wailsapp/wails/runtime" + + "github.com/wailsapp/wails/v2/internal/bridge" + "github.com/wailsapp/wails/v2/internal/menumanager" + + "github.com/wailsapp/wails/v2/pkg/options" + + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher" + "github.com/wailsapp/wails/v2/internal/servicebus" + "github.com/wailsapp/wails/v2/internal/signal" + "github.com/wailsapp/wails/v2/internal/subsystem" +) + +// App defines a Wails application structure +type App struct { + appType string + + servicebus *servicebus.ServiceBus + logger *logger.Logger + signal *signal.Manager + options *options.App + + // Subsystems + log *subsystem.Log + runtime *subsystem.Runtime + event *subsystem.Event + //binding *subsystem.Binding + call *subsystem.Call + menu *subsystem.Menu + dispatcher *messagedispatcher.Dispatcher + + menuManager *menumanager.Manager + + // Indicates if the app is in debug mode + debug bool + + // This is our binding DB + bindings *binding.Bindings + + // Application Stores + loglevelStore *runtime.Store + appconfigStore *runtime.Store + + // OnStartup/OnShutdown + startupCallback func(*runtime.Runtime) + shutdownCallback func() + + // Bridge + bridge *bridge.Bridge +} + +// Create App +func CreateApp(appoptions *options.App) (*App, error) { + + // Merge default options + options.MergeDefaults(appoptions) + + // Set up logger + myLogger := logger.New(appoptions.Logger) + + // Create the menu manager + menuManager := menumanager.NewManager() + + // Process the application menu + menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions)) + + // Process context menus + contextMenus := options.GetContextMenus(appoptions) + for _, contextMenu := range contextMenus { + menuManager.AddContextMenu(contextMenu) + } + + // Process tray menus + trayMenus := options.GetTrayMenus(appoptions) + for _, trayMenu := range trayMenus { + menuManager.AddTrayMenu(trayMenu) + } + + // Create binding exemptions - Ugly hack. There must be a better way + bindingExemptions := []interface{}{appoptions.OnStartup, appoptions.OnShutdown, appoptions.OnDomReady} + + result := &App{ + appType: "dev", + bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions), + logger: myLogger, + servicebus: servicebus.New(myLogger), + startupCallback: appoptions.OnStartup, + shutdownCallback: appoptions.OnShutdown, + bridge: bridge.NewBridge(myLogger), + menuManager: menuManager, + } + + result.options = appoptions + + // Initialise the app + err := result.Init() + + return result, err + +} + +// Run the application +func (a *App) Run() error { + + var err error + + // Setup a context + var subsystemWaitGroup sync.WaitGroup + parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup) + ctx, cancel := context.WithCancel(parentContext) + defer cancel() + + // Start the service bus + a.servicebus.Debug() + err = a.servicebus.Start() + if err != nil { + return err + } + + runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback) + if err != nil { + return err + } + a.runtime = runtimesubsystem + err = a.runtime.Start() + if err != nil { + return err + } + + // Application Stores + a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel) + a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options) + + // Start the logging subsystem + log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore) + if err != nil { + return err + } + a.log = log + err = a.log.Start() + if err != nil { + return err + } + + // create the dispatcher + dispatcher, err := messagedispatcher.New(a.servicebus, a.logger) + if err != nil { + return err + } + a.dispatcher = dispatcher + err = dispatcher.Start() + if err != nil { + return err + } + + // Start the eventing subsystem + eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger) + if err != nil { + return err + } + a.event = eventsubsystem + err = a.event.Start() + if err != nil { + return err + } + + // Start the menu subsystem + menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager) + if err != nil { + return err + } + a.menu = menusubsystem + err = a.menu.Start() + if err != nil { + return err + } + + // Start the call subsystem + callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB()) + if err != nil { + return err + } + a.call = callSubsystem + err = a.call.Start() + if err != nil { + return err + } + + // Dump bindings as a debug + bindingDump, err := a.bindings.ToJSON() + if err != nil { + return err + } + + // Generate backend.js + a.bindings.GenerateBackendJS() + + // Setup signal handler + signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger) + if err != nil { + return err + } + a.signal = signalsubsystem + a.signal.Start() + + err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug) + a.logger.Trace("Bridge.Run() exited") + if err != nil { + return err + } + + // Close down all the subsystems + a.logger.Trace("Cancelling subsystems") + cancel() + subsystemWaitGroup.Wait() + + a.logger.Trace("Cancelling dispatcher") + dispatcher.Close() + + // Close log + a.logger.Trace("Stopping log") + log.Close() + + a.logger.Trace("Stopping Service bus") + err = a.servicebus.Stop() + if err != nil { + return err + } + + // OnShutdown callback + if a.shutdownCallback != nil { + a.shutdownCallback() + } + return nil + +} +*/ diff --git a/v2/internal/app/hybrid.go b/v2/internal/app/hybrid.go new file mode 100644 index 000000000..8c95c2879 --- /dev/null +++ b/v2/internal/app/hybrid.go @@ -0,0 +1,194 @@ +// +build !server,!desktop,hybrid + +package app + +import ( + "os" + "path/filepath" + + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/ffenestri" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher" + "github.com/wailsapp/wails/v2/internal/servicebus" + "github.com/wailsapp/wails/v2/internal/subsystem" + "github.com/wailsapp/wails/v2/internal/webserver" +) + +// Config defines the Application's configuration +type Config struct { + Title string // Title is the value to be displayed in the title bar + Width int // Width is the desired window width + Height int // Height is the desired window height + DevTools bool // DevTools enables or disables the browser development tools + Resizable bool // Resizable when False prevents window resizing + ServerEnabled bool // ServerEnabled when True allows remote connections +} + +// App defines a Wails application structure +type App struct { + config Config + window *ffenestri.Application + webserver *webserver.WebServer + binding *subsystem.Binding + call *subsystem.Call + event *subsystem.Event + log *subsystem.Log + runtime *subsystem.Runtime + + bindings *binding.Bindings + logger *logger.Logger + dispatcher *messagedispatcher.Dispatcher + servicebus *servicebus.ServiceBus + + debug bool +} + +// Create App +func CreateApp(options *Options) *App { + + // Merge default options + options.mergeDefaults() + + // Set up logger + myLogger := logger.New(os.Stdout) + myLogger.SetLogLevel(logger.INFO) + + window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{ + Title: options.Title, + Width: options.Width, + Height: options.Height, + MinWidth: options.MinWidth, + MinHeight: options.MinHeight, + MaxWidth: options.MaxWidth, + MaxHeight: options.MaxHeight, + StartHidden: options.StartHidden, + DevTools: options.DevTools, + + Resizable: !options.DisableResize, + Fullscreen: options.Fullscreen, + }, myLogger) + + app := &App{ + window: window, + webserver: webserver.NewWebServer(myLogger), + servicebus: servicebus.New(myLogger), + logger: myLogger, + bindings: binding.NewBindings(myLogger, options.Bind), + } + + // Initialise the app + app.Init() + + return app +} + +// Run the application +func (a *App) Run() error { + + // Default app options + var port = 8080 + var ip = "localhost" + var suppressLogging = false + + // Create CLI + cli := clir.NewCli(filepath.Base(os.Args[0]), "Desktop/Server Build", "") + + // Setup flags + cli.IntFlag("p", "Port to serve on", &port) + cli.StringFlag("i", "IP to serve on", &ip) + cli.BoolFlag("q", "Suppress logging", &suppressLogging) + + // Setup main action + cli.Action(func() error { + + // Set IP + Port + a.webserver.SetPort(port) + a.webserver.SetIP(ip) + a.webserver.SetBindings(a.bindings) + // Log information (if we aren't suppressing it) + if !suppressLogging { + cli.PrintBanner() + a.logger.Info("Running server at %s", a.webserver.URL()) + } + + a.servicebus.Start() + log, err := subsystem.NewLog(a.servicebus, a.logger) + if err != nil { + return err + } + a.log = log + a.log.Start() + dispatcher, err := messagedispatcher.New(a.servicebus, a.logger) + if err != nil { + return err + } + a.dispatcher = dispatcher + a.dispatcher.Start() + + // Start the runtime + runtime, err := subsystem.NewRuntime(a.servicebus, a.logger) + if err != nil { + return err + } + a.runtime = runtime + a.runtime.Start() + + // Start the binding subsystem + binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime()) + if err != nil { + return err + } + a.binding = binding + a.binding.Start() + + // Start the eventing subsystem + event, err := subsystem.NewEvent(a.servicebus, a.logger) + if err != nil { + return err + } + a.event = event + a.event.Start() + + // Start the call subsystem + call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB()) + if err != nil { + return err + } + a.call = call + a.call.Start() + + // Required so that the WailsInit functions are fired! + runtime.GoRuntime().Events.Emit("wails:loaded") + + // Set IP + Port + a.webserver.SetPort(port) + a.webserver.SetIP(ip) + + // Log information (if we aren't suppressing it) + if !suppressLogging { + cli.PrintBanner() + println("Running server at " + a.webserver.URL()) + } + + // Dump bindings as a debug + bindingDump, err := a.bindings.ToJSON() + if err != nil { + return err + } + + go func() { + if err := a.webserver.Start(dispatcher, event); err != nil { + a.logger.Error("Webserver failed to start %s", err) + } + }() + + result := a.window.Run(dispatcher, bindingDump) + a.servicebus.Stop() + + return result + }) + + return cli.Run() +} diff --git a/v2/internal/app/preflight_default.go b/v2/internal/app/preflight_default.go new file mode 100644 index 000000000..5508a7748 --- /dev/null +++ b/v2/internal/app/preflight_default.go @@ -0,0 +1,9 @@ +//+build !windows + +package app + +import "github.com/wailsapp/wails/v2/pkg/options" + +func (a *App) PreflightChecks(options *options.App) error { + return nil +} diff --git a/v2/internal/app/preflight_windows.go b/v2/internal/app/preflight_windows.go new file mode 100644 index 000000000..29bbb7a74 --- /dev/null +++ b/v2/internal/app/preflight_windows.go @@ -0,0 +1,26 @@ +//+build windows + +package app + +import ( + "github.com/wailsapp/wails/v2/internal/ffenestri/windows/wv2runtime" + "github.com/wailsapp/wails/v2/pkg/options" +) + +func (a *App) PreflightChecks(options *options.App) error { + + _ = options + + // Process the webview2 runtime situation. We can pass a strategy in via the `webview2` flag for `wails build`. + // This will determine how wv2runtime.Process will handle a lack of valid runtime. + installedVersion, err := wv2runtime.Process() + if installedVersion != nil { + a.logger.Debug("WebView2 Runtime installed: Name: '%s' Version:'%s' Location:'%s'. Minimum version required: %s.", + installedVersion.Name, installedVersion.Version, installedVersion.Location, wv2runtime.MinimumRuntimeVersion) + } + if err != nil { + return err + } + + return nil +} diff --git a/v2/internal/app/production.go b/v2/internal/app/production.go new file mode 100644 index 000000000..9a2554826 --- /dev/null +++ b/v2/internal/app/production.go @@ -0,0 +1,12 @@ +//go:build production +// +build production + +package app + +import "github.com/wailsapp/wails/v2/pkg/logger" + +// Init initialises the application for a production environment +func (a *App) Init() error { + a.logger.SetLogLevel(logger.ERROR) + return nil +} diff --git a/v2/internal/app/server.go b/v2/internal/app/server.go new file mode 100644 index 000000000..06734f0ad --- /dev/null +++ b/v2/internal/app/server.go @@ -0,0 +1,165 @@ +//go:build server && !desktop +// +build server,!desktop + +package app + +import ( + "context" + "os" + "path/filepath" + + "github.com/wailsapp/wails/v2/pkg/options" + + "github.com/leaanthony/clir" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher" + "github.com/wailsapp/wails/v2/internal/servicebus" + "github.com/wailsapp/wails/v2/internal/subsystem" + "github.com/wailsapp/wails/v2/internal/webserver" +) + +// App defines a Wails application structure +type App struct { + appType string + + binding *subsystem.Binding + call *subsystem.Call + event *subsystem.Event + log *subsystem.Log + + options *options.App + + bindings *binding.Bindings + logger *logger.Logger + dispatcher *messagedispatcher.Dispatcher + servicebus *servicebus.ServiceBus + webserver *webserver.WebServer + + debug bool + + // OnStartup/OnShutdown + startupCallback func(ctx context.Context) + shutdownCallback func() +} + +// Create App +func CreateApp(appoptions *options.App) (*App, error) { + + // Merge default options + options.MergeDefaults(appoptions) + + // Set up logger + myLogger := logger.New(appoptions.Logger) + myLogger.SetLogLevel(appoptions.LogLevel) + + result := &App{ + appType: "server", + bindings: binding.NewBindings(myLogger, options.Bind), + logger: myLogger, + servicebus: servicebus.New(myLogger), + webserver: webserver.NewWebServer(myLogger), + startupCallback: appoptions.OnStartup, + shutdownCallback: appoptions.OnShutdown, + } + + // Initialise app + result.Init() + + return result, nil +} + +// Run the application +func (a *App) Run() error { + + // Default app options + var port = 8080 + var ip = "localhost" + var SuppressLogging = false + var debugMode = false + + // Create CLI + cli := clir.NewCli(filepath.Base(os.Args[0]), "Server Build", "") + + // Setup flags + cli.IntFlag("p", "Port to serve on", &port) + cli.StringFlag("i", "IP to serve on", &ip) + cli.BoolFlag("d", "Debug mode", &debugMode) + cli.BoolFlag("q", "Suppress logging", &SuppressLogging) + + // Setup main action + cli.Action(func() error { + + // Set IP + Port + a.webserver.SetPort(port) + a.webserver.SetIP(ip) + a.webserver.SetBindings(a.bindings) + // Log information (if we aren't Suppressing it) + if !SuppressLogging { + cli.PrintBanner() + a.logger.Info("Running server at %s", a.webserver.URL()) + } + + if debugMode { + a.servicebus.Debug() + } + + // Start the runtime + runtime, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback) + if err != nil { + return err + } + a.runtime = runtime + a.runtime.Start() + + a.servicebus.Start() + log, err := subsystem.NewLog(a.servicebus, a.logger) + if err != nil { + return err + } + a.log = log + a.log.Start() + dispatcher, err := messagedispatcher.New(a.servicebus, a.logger) + if err != nil { + return err + } + a.dispatcher = dispatcher + a.dispatcher.Start() + + // Start the binding subsystem + binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings) + if err != nil { + return err + } + a.binding = binding + a.binding.Start() + + // Start the eventing subsystem + event, err := subsystem.NewEvent(a.servicebus, a.logger) + if err != nil { + return err + } + a.event = event + a.event.Start() + + // Start the call subsystem + call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime()) + if err != nil { + return err + } + a.call = call + a.call.Start() + + // Required so that the WailsInit functions are fired! + runtime.GoRuntime().Events.Emit("wails:loaded") + + if err := a.webserver.Start(dispatcher, event); err != nil { + a.logger.Error("Webserver failed to start %s", err) + return err + } + + return nil + }) + + return cli.Run() +} diff --git a/v2/internal/appng/app_bindings.go b/v2/internal/appng/app_bindings.go new file mode 100644 index 000000000..e314802c9 --- /dev/null +++ b/v2/internal/appng/app_bindings.go @@ -0,0 +1,105 @@ +//go:build bindings +// +build bindings + +package appng + +import ( + "github.com/leaanthony/gosod" + "github.com/wailsapp/wails/v2/internal/binding" + wailsRuntime "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/wailsapp/wails/v2/internal/frontend/runtime/wrapper" + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/project" + "github.com/wailsapp/wails/v2/pkg/options" + "os" + "path/filepath" +) + +// App defines a Wails application structure +type App struct { + logger *logger.Logger + appoptions *options.App +} + +func (a *App) Run() error { + + // Create binding exemptions - Ugly hack. There must be a better way + bindingExemptions := []interface{}{a.appoptions.OnStartup, a.appoptions.OnShutdown, a.appoptions.OnDomReady} + appBindings := binding.NewBindings(a.logger, a.appoptions.Bind, bindingExemptions) + + err := generateBindings(appBindings) + if err != nil { + return err + } + return nil +} + +// CreateApp creates the app! +func CreateApp(appoptions *options.App) (*App, error) { + // Set up logger + myLogger := logger.New(appoptions.Logger) + myLogger.SetLogLevel(appoptions.LogLevel) + + result := &App{ + logger: myLogger, + appoptions: appoptions, + } + + return result, nil + +} + +func generateBindings(bindings *binding.Bindings) error { + + cwd, err := os.Getwd() + if err != nil { + return err + } + projectConfig, err := project.Load(cwd) + if err != nil { + return err + } + + wrapperDir := filepath.Join(projectConfig.WailsJSDir, "wailsjs", "runtime") + _ = os.RemoveAll(wrapperDir) + extractor := gosod.New(wrapper.RuntimeWrapper) + err = extractor.Extract(wrapperDir, nil) + if err != nil { + return err + } + + //ipcdev.js + err = os.WriteFile(filepath.Join(wrapperDir, "ipcdev.js"), wailsRuntime.DesktopIPC, 0755) + if err != nil { + return err + } + //runtimedev.js + err = os.WriteFile(filepath.Join(wrapperDir, "runtimedev.js"), wailsRuntime.RuntimeDesktopJS, 0755) + if err != nil { + return err + } + + targetDir := filepath.Join(projectConfig.WailsJSDir, "wailsjs", "go") + err = os.RemoveAll(targetDir) + if err != nil { + return err + } + _ = fs.MkDirs(targetDir) + + modelsFile := filepath.Join(targetDir, "models.ts") + err = bindings.WriteTS(modelsFile) + if err != nil { + return err + } + + // Write backend method wrappers + bindingsFilename := filepath.Join(targetDir, "bindings.js") + err = bindings.GenerateBackendJS(bindingsFilename, true) + if err != nil { + return err + } + + return nil + +} diff --git a/v2/internal/appng/app_darwin.go b/v2/internal/appng/app_darwin.go new file mode 100644 index 000000000..b6f5a16a4 --- /dev/null +++ b/v2/internal/appng/app_darwin.go @@ -0,0 +1,15 @@ +//go:build darwin && !bindings + +package appng + +import ( + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options" +) + +func PreflightChecks(options *options.App, logger *logger.Logger) error { + + _ = options + + return nil +} diff --git a/v2/internal/appng/app_default_darwin.go b/v2/internal/appng/app_default_darwin.go new file mode 100644 index 000000000..f0971f864 --- /dev/null +++ b/v2/internal/appng/app_default_darwin.go @@ -0,0 +1,32 @@ +//go:build !dev && !production && !bindings && darwin + +package appng + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/pkg/options" +) + +// App defines a Wails application structure +type App struct{} + +func (a *App) Run() error { + return nil +} + +// CreateApp creates the app! +func CreateApp(_ *options.App) (*App, error) { + // result := w32.MessageBox(0, + // `Wails applications will not build without the correct build tags. + //Please use "wails build" or press "OK" to open the documentation on how to use "go build"`, + // "Error", + // w32.MB_ICONERROR|w32.MB_OKCANCEL) + // if result == 1 { + // exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://wails.io").Start() + // } + + err := fmt.Errorf(`Wails applications will not build without the correct build tags.`) + + return nil, err +} diff --git a/v2/internal/appng/app_default_windows.go b/v2/internal/appng/app_default_windows.go new file mode 100644 index 000000000..64ac79aa7 --- /dev/null +++ b/v2/internal/appng/app_default_windows.go @@ -0,0 +1,30 @@ +//go:build !dev && !production && !bindings && windows + +package appng + +import ( + "os/exec" + + "github.com/leaanthony/winc/w32" + "github.com/wailsapp/wails/v2/pkg/options" +) + +// App defines a Wails application structure +type App struct{} + +func (a *App) Run() error { + return nil +} + +// CreateApp creates the app! +func CreateApp(_ *options.App) (*App, error) { + result := w32.MessageBox(0, + `Wails applications will not build without the correct build tags. +Please use "wails build" or press "OK" to open the documentation on how to use "go build"`, + "Error", + w32.MB_ICONERROR|w32.MB_OKCANCEL) + if result == 1 { + exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://wails.io/docs/guides/manual-builds").Start() + } + return nil, nil +} diff --git a/v2/internal/appng/app_dev.go b/v2/internal/appng/app_dev.go new file mode 100644 index 000000000..cc463bb88 --- /dev/null +++ b/v2/internal/appng/app_dev.go @@ -0,0 +1,171 @@ +//go:build dev +// +build dev + +package appng + +import ( + "context" + "flag" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/frontend/desktop" + "github.com/wailsapp/wails/v2/internal/frontend/devserver" + "github.com/wailsapp/wails/v2/internal/frontend/dispatcher" + "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/menumanager" + "github.com/wailsapp/wails/v2/internal/project" + "github.com/wailsapp/wails/v2/internal/signal" + pkglogger "github.com/wailsapp/wails/v2/pkg/logger" + "github.com/wailsapp/wails/v2/pkg/options" + "os" + "path/filepath" +) + +// App defines a Wails application structure +type App struct { + frontend frontend.Frontend + logger *logger.Logger + signal *signal.Manager + options *options.App + + menuManager *menumanager.Manager + + // Indicates if the app is in debug mode + debug bool + + // OnStartup/OnShutdown + startupCallback func(ctx context.Context) + shutdownCallback func(ctx context.Context) + ctx context.Context +} + +func (a *App) Run() error { + err := a.frontend.Run(a.ctx) + if a.shutdownCallback != nil { + a.shutdownCallback(a.ctx) + } + return err +} + +// CreateApp creates the app! +func CreateApp(appoptions *options.App) (*App, error) { + var err error + + ctx := context.WithValue(context.Background(), "debug", true) + + // Set up logger + myLogger := logger.New(appoptions.Logger) + myLogger.SetLogLevel(appoptions.LogLevel) + + // Check for CLI Flags + assetdir := flag.String("assetdir", "", "Directory to serve assets") + devServerURL := flag.String("devserverurl", "", "URL of development server") + loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error") + flag.Parse() + if devServerURL != nil && *devServerURL != "" { + ctx = context.WithValue(ctx, "devserverurl", *devServerURL) + } + if assetdir != nil && *assetdir != "" { + ctx = context.WithValue(ctx, "assetdir", *assetdir) + } + + if loglevel != nil && *loglevel != "" { + level, err := pkglogger.StringToLogLevel(*loglevel) + if err != nil { + return nil, err + } + myLogger.SetLogLevel(level) + } + + // Attach logger to context + ctx = context.WithValue(ctx, "logger", myLogger) + + // Preflight checks + err = PreflightChecks(appoptions, myLogger) + if err != nil { + return nil, err + } + + // Merge default options + options.MergeDefaults(appoptions) + + var menuManager *menumanager.Manager + + // Process the application menu + if appoptions.Menu != nil { + // Create the menu manager + menuManager = menumanager.NewManager() + err = menuManager.SetApplicationMenu(appoptions.Menu) + if err != nil { + return nil, err + } + } + + // Create binding exemptions - Ugly hack. There must be a better way + bindingExemptions := []interface{}{appoptions.OnStartup, appoptions.OnShutdown, appoptions.OnDomReady} + appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions) + + err = generateBindings(appBindings) + if err != nil { + return nil, err + } + eventHandler := runtime.NewEvents(myLogger) + ctx = context.WithValue(ctx, "events", eventHandler) + messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings, eventHandler) + + // Create the frontends and register to event handler + desktopFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) + appFrontend := devserver.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher, menuManager, desktopFrontend) + eventHandler.AddFrontend(appFrontend) + eventHandler.AddFrontend(desktopFrontend) + + result := &App{ + ctx: ctx, + frontend: appFrontend, + logger: myLogger, + menuManager: menuManager, + startupCallback: appoptions.OnStartup, + shutdownCallback: appoptions.OnShutdown, + debug: true, + } + + result.options = appoptions + + return result, nil + +} + +func generateBindings(bindings *binding.Bindings) error { + + cwd, err := os.Getwd() + if err != nil { + return err + } + projectConfig, err := project.Load(cwd) + if err != nil { + return err + } + + targetDir := filepath.Join(projectConfig.WailsJSDir, "wailsjs", "go") + err = os.RemoveAll(targetDir) + if err != nil { + return err + } + _ = fs.MkDirs(targetDir) + modelsFile := filepath.Join(targetDir, "models.ts") + err = bindings.WriteTS(modelsFile) + if err != nil { + return err + } + + // Write backend method wrappers + bindingsFilename := filepath.Join(targetDir, "bindings.js") + err = bindings.GenerateBackendJS(bindingsFilename, false) + if err != nil { + return err + } + return nil + +} diff --git a/v2/internal/appng/app_production.go b/v2/internal/appng/app_production.go new file mode 100644 index 000000000..7e404d359 --- /dev/null +++ b/v2/internal/appng/app_production.go @@ -0,0 +1,101 @@ +//go:build production +// +build production + +package appng + +import ( + "context" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/frontend/desktop" + "github.com/wailsapp/wails/v2/internal/frontend/dispatcher" + "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/menumanager" + "github.com/wailsapp/wails/v2/internal/signal" + "github.com/wailsapp/wails/v2/pkg/options" +) + +// App defines a Wails application structure +type App struct { + frontend frontend.Frontend + logger *logger.Logger + signal *signal.Manager + options *options.App + + menuManager *menumanager.Manager + + // Indicates if the app is in debug mode + debug bool + + // OnStartup/OnShutdown + startupCallback func(ctx context.Context) + shutdownCallback func(ctx context.Context) + ctx context.Context +} + +func (a *App) Run() error { + err := a.frontend.Run(a.ctx) + if a.shutdownCallback != nil { + a.shutdownCallback(a.ctx) + } + return err +} + +// CreateApp creates the app! +func CreateApp(appoptions *options.App) (*App, error) { + var err error + + ctx := context.Background() + + // Merge default options + options.MergeDefaults(appoptions) + + // Set up logger + myLogger := logger.New(appoptions.Logger) + myLogger.SetLogLevel(appoptions.LogLevel) + + // Preflight Checks + err = PreflightChecks(appoptions, myLogger) + if err != nil { + return nil, err + } + + // Create the menu manager + menuManager := menumanager.NewManager() + + // Process the application menu + if appoptions.Menu != nil { + err = menuManager.SetApplicationMenu(appoptions.Menu) + if err != nil { + return nil, err + } + } + + // Create binding exemptions - Ugly hack. There must be a better way + bindingExemptions := []interface{}{appoptions.OnStartup, appoptions.OnShutdown, appoptions.OnDomReady} + appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions) + eventHandler := runtime.NewEvents(myLogger) + ctx = context.WithValue(ctx, "events", eventHandler) + messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings, eventHandler) + + appFrontend := desktop.NewFrontend(ctx, appoptions, myLogger, appBindings, messageDispatcher) + eventHandler.AddFrontend(appFrontend) + + result := &App{ + ctx: ctx, + frontend: appFrontend, + logger: myLogger, + menuManager: menuManager, + startupCallback: appoptions.OnStartup, + shutdownCallback: appoptions.OnShutdown, + debug: false, + } + + result.options = appoptions + + result.ctx = context.WithValue(result.ctx, "debug", result.debug) + + return result, nil + +} diff --git a/v2/internal/appng/app_windows.go b/v2/internal/appng/app_windows.go new file mode 100644 index 000000000..644cd728e --- /dev/null +++ b/v2/internal/appng/app_windows.go @@ -0,0 +1,27 @@ +//go:build windows && !bindings + +package appng + +import ( + "github.com/wailsapp/wails/v2/internal/ffenestri/windows/wv2runtime" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options" +) + +func PreflightChecks(options *options.App, logger *logger.Logger) error { + + _ = options + + // Process the webview2 runtime situation. We can pass a strategy in via the `webview2` flag for `wails build`. + // This will determine how wv2runtime.Process will handle a lack of valid runtime. + installedVersion, err := wv2runtime.Process() + if installedVersion != nil { + logger.Debug("WebView2 Runtime installed: Name: '%s' Version:'%s' Location:'%s'. Minimum version required: %s.", + installedVersion.Name, installedVersion.Version, installedVersion.Location, wv2runtime.MinimumRuntimeVersion) + } + if err != nil { + return err + } + + return nil +} diff --git a/v2/internal/assetdb/assetdb.go b/v2/internal/assetdb/assetdb.go new file mode 100644 index 000000000..54892952e --- /dev/null +++ b/v2/internal/assetdb/assetdb.go @@ -0,0 +1,112 @@ +package assetdb + +import ( + "fmt" + "strings" + "unsafe" +) + +// AssetDB is a database for assets encoded as byte slices +type AssetDB struct { + db map[string][]byte +} + +// NewAssetDB creates a new AssetDB and initialises a blank db +func NewAssetDB() *AssetDB { + return &AssetDB{ + db: make(map[string][]byte), + } +} + +// AddAsset saves the given byte slice under the given name +func (a *AssetDB) AddAsset(name string, data []byte) { + a.db[name] = data +} + +// Remove removes the named asset +func (a *AssetDB) Remove(name string) { + delete(a.db, name) +} + +// Asset retrieves the byte slice for the given name +func (a *AssetDB) Read(name string) ([]byte, error) { + result := a.db[name] + if result == nil { + return nil, fmt.Errorf("asset '%s' not found", name) + } + return result, nil +} + +// AssetAsString returns the asset as a string. +// It also returns a boolean indicating whether the asset existed or not. +func (a *AssetDB) String(name string) (string, error) { + asset, err := a.Read(name) + if err != nil { + return "", err + } + return *(*string)(unsafe.Pointer(&asset)), nil +} + +func (a *AssetDB) Dump() { + fmt.Printf("Assets:\n") + for k, _ := range a.db { + fmt.Println(k) + } +} + +// Serialize converts the entire database to a file that when compiled will +// reconstruct the AssetDB during init() +// name: name of the asset.AssetDB instance +// pkg: package name placed at the top of the file +func (a *AssetDB) Serialize(name, pkg string) string { + var cdata strings.Builder + // Set buffer size to 4k + cdata.Grow(4096) + + // Write header + header := `// DO NOT EDIT - Generated automatically +package %s + +import "github.com/wailsapp/wails/v2/internal/assetdb" + +var ( + %s *assetdb.AssetDB = assetdb.NewAssetDB() +) + +// AssetsDB is a clean interface to the assetdb.AssetDB struct +type AssetsDB interface { + Read(string) ([]byte, error) + String(string) (string, error) +} + +// Assets returns the asset database +func Assets() AssetsDB { + return %s +} + +func init() { +` + cdata.WriteString(fmt.Sprintf(header, pkg, name, name)) + + for aname, bytes := range a.db { + cdata.WriteString(fmt.Sprintf("\t%s.AddAsset(\"%s\", []byte{", + name, + aname)) + + l := len(bytes) + if l == 0 { + cdata.WriteString("0x00})\n") + continue + } + + // Convert each byte to hex + for _, b := range bytes[:l-1] { + cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + } + cdata.WriteString(fmt.Sprintf("0x%x})\n", bytes[l-1])) + } + + cdata.WriteString(`}`) + + return cdata.String() +} diff --git a/v2/internal/assetdb/assetdb_test.go b/v2/internal/assetdb/assetdb_test.go new file mode 100644 index 000000000..b3f34b9fd --- /dev/null +++ b/v2/internal/assetdb/assetdb_test.go @@ -0,0 +1,70 @@ +package assetdb + +import "testing" +import "github.com/matryer/is" + +func TestExistsAsBytes(t *testing.T) { + + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("hello", helloworld) + + result, err := db.Read("hello") + + is.True(err == nil) + is.Equal(result, helloworld) +} + +func TestNotExistsAsBytes(t *testing.T) { + + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("hello4", helloworld) + + result, err := db.Read("hello") + + is.True(err != nil) + is.True(result == nil) +} + +func TestExistsAsString(t *testing.T) { + + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("hello", helloworld) + + result, err := db.String("hello") + + // Ensure it exists + is.True(err == nil) + + // Ensure the string is the same as the byte slice + is.Equal(result, "Hello, World!") +} + +func TestNotExistsAsString(t *testing.T) { + + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("hello", helloworld) + + result, err := db.String("help") + + // Ensure it doesn't exist + is.True(err != nil) + + // Ensure the string is blank + is.Equal(result, "") +} diff --git a/v2/internal/assetdb/filesystem.go b/v2/internal/assetdb/filesystem.go new file mode 100644 index 000000000..48acb713f --- /dev/null +++ b/v2/internal/assetdb/filesystem.go @@ -0,0 +1,176 @@ +// +build !desktop +package assetdb + +import ( + "errors" + "io" + "net/http" + "os" + "path" + "sort" + "strings" + "time" +) + +var errWhence = errors.New("Seek: invalid whence") +var errOffset = errors.New("Seek: invalid offset") + +// Open implements the http.FileSystem interface for the AssetDB +func (a *AssetDB) Open(name string) (http.File, error) { + if name == "/" || name == "" { + return &Entry{name: "/", dir: true, db: a}, nil + } + + if data, ok := a.db[name]; ok { + return &Entry{name: name, data: data, size: len(data)}, nil + } else { + for n, _ := range a.db { + if strings.HasPrefix(n, name+"/") { + return &Entry{name: name, db: a, dir: true}, nil + } + } + } + return &Entry{}, os.ErrNotExist +} + +// readdir returns the directory entries for the requested directory +func (a *AssetDB) readdir(name string) ([]os.FileInfo, error) { + dir := name + var ents []string + + fim := make(map[string]os.FileInfo) + for fn, data := range a.db { + if strings.HasPrefix(fn, dir) { + pieces := strings.Split(fn[len(dir)+1:], "/") + if len(pieces) == 1 { + fim[pieces[0]] = FI{name: pieces[0], dir: false, size: len(data)} + ents = append(ents, pieces[0]) + } else { + fim[pieces[0]] = FI{name: pieces[0], dir: true, size: -1} + ents = append(ents, pieces[0]) + } + } + } + + if len(ents) == 0 { + return nil, os.ErrNotExist + } + + sort.Strings(ents) + var list []os.FileInfo + for _, dir := range ents { + list = append(list, fim[dir]) + } + return list, nil +} + +// Entry implements the http.File interface to allow for +// use in the http.FileSystem implementation of AssetDB +type Entry struct { + name string + data []byte + dir bool + size int + db *AssetDB + off int +} + +// Close is a noop +func (e Entry) Close() error { + return nil +} + +// Read fills the slice provided returning how many bytes were written +// and any errors encountered +func (e *Entry) Read(p []byte) (n int, err error) { + if e.off >= e.size { + return 0, io.EOF + } + numout := len(p) + if numout > e.size { + numout = e.size + } + for i := 0; i < numout; i++ { + p[i] = e.data[e.off+i] + } + e.off += numout + n = int(numout) + err = nil + return +} + +// Seek seeks to the specified offset from the location specified by whence +func (e *Entry) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, errWhence + case io.SeekStart: + offset += 0 + case io.SeekCurrent: + offset += int64(e.off) + case io.SeekEnd: + offset += int64(e.size) + } + + if offset < 0 { + return 0, errOffset + } + e.off = int(offset) + return offset, nil +} + +// Readdir returns the directory entries inside this entry if it is a directory +func (e Entry) Readdir(count int) ([]os.FileInfo, error) { + ents := []os.FileInfo{} + if !e.dir { + return ents, errors.New("Not a directory") + } + return e.db.readdir(e.name) +} + +// Stat returns information about this directory entry +func (e Entry) Stat() (os.FileInfo, error) { + return FI{e.name, e.size, e.dir}, nil +} + +// FI is the AssetDB implementation of os.FileInfo. +type FI struct { + name string + size int + dir bool +} + +// IsDir returns true if this is a directory +func (fi FI) IsDir() bool { + return fi.dir +} + +// ModTime always returns now +func (fi FI) ModTime() time.Time { + return time.Time{} +} + +// Mode returns the file as readonly and directories +// as world writeable and executable +func (fi FI) Mode() os.FileMode { + if fi.IsDir() { + return 0755 | os.ModeDir + } + return 0444 +} + +// Name returns the name of this object without +// any leading slashes +func (fi FI) Name() string { + return path.Base(fi.name) +} + +// Size returns the size of this item +func (fi FI) Size() int64 { + return int64(fi.size) +} + +// Sys returns nil +func (fi FI) Sys() interface{} { + return nil +} diff --git a/v2/internal/assetdb/filesystem_test.go b/v2/internal/assetdb/filesystem_test.go new file mode 100644 index 000000000..1c2ed94a4 --- /dev/null +++ b/v2/internal/assetdb/filesystem_test.go @@ -0,0 +1,108 @@ +package assetdb + +import ( + "fmt" + "os" + "testing" + + "github.com/matryer/is" +) + +func TestOpenLeadingSlash(t *testing.T) { + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("/hello", helloworld) + + file, err := db.Open("/hello") + // Ensure it does exist + is.True(err == nil) + + buff := make([]byte, len(helloworld)) + n, err := file.Read(buff) + fmt.Printf("Error %v\n", err) + is.True(err == nil) + is.Equal(n, len(helloworld)) + result := string(buff) + + // Ensure the string is blank + is.Equal(result, string(helloworld)) +} + +func TestOpen(t *testing.T) { + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("/hello", helloworld) + + file, err := db.Open("/hello") + + // Ensure it does exist + is.True(err == nil) + + buff := make([]byte, len(helloworld)) + n, err := file.Read(buff) + is.True(err == nil) + is.Equal(n, len(helloworld)) + result := string(buff) + + // Ensure the string is blank + is.Equal(result, string(helloworld)) +} + +func TestReaddir(t *testing.T) { + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("/hello", helloworld) + db.AddAsset("/directory/hello", helloworld) + db.AddAsset("/directory/subdirectory/hello", helloworld) + + dir, err := db.Open("/doesntexist") + is.True(err == os.ErrNotExist) + ents, err := dir.Readdir(-1) + is.Equal([]os.FileInfo{}, ents) + + dir, err = db.Open("/") + is.True(dir != nil) + is.True(err == nil) + ents, err = dir.Readdir(-1) + is.True(err == nil) + is.Equal(3, len(ents)) +} + +func TestReaddirSubdirectory(t *testing.T) { + is := is.New(t) + + var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33} + + db := NewAssetDB() + db.AddAsset("/hello", helloworld) + db.AddAsset("/directory/hello", helloworld) + db.AddAsset("/directory/subdirectory/hello", helloworld) + + expected := []os.FileInfo{ + FI{name: "hello", dir: false, size: len(helloworld)}, + FI{name: "subdirectory", dir: true, size: -1}, + } + + dir, err := db.Open("/directory") + is.True(dir != nil) + is.True(err == nil) + ents, err := dir.Readdir(-1) + is.Equal(expected, ents) + + // Check sub-subdirectory + dir, err = db.Open("/directory/subdirectory") + is.True(dir != nil) + is.True(err == nil) + ents, err = dir.Readdir(-1) + is.True(err == nil) + is.Equal([]os.FileInfo{FI{name: "hello", size: len(helloworld)}}, ents) +} diff --git a/v2/internal/bind/bind.go b/v2/internal/bind/bind.go new file mode 100644 index 000000000..156fd4ce4 --- /dev/null +++ b/v2/internal/bind/bind.go @@ -0,0 +1,9 @@ +package bind + +func IsStructPointer(value interface{}) bool { + switch t := value.(type) { + default: + println(t) + } + return false +} diff --git a/v2/internal/binding/assets/package.json b/v2/internal/binding/assets/package.json new file mode 100644 index 000000000..1b82716c0 --- /dev/null +++ b/v2/internal/binding/assets/package.json @@ -0,0 +1,10 @@ +{ + "name": "go", + "version": "1.0.0", + "description": "Package to wrap your bound go methods", + "main": "bindings.js", + "types": "bindings.d.ts", + "scripts": {}, + "author": "", + "license": "ISC" +} \ No newline at end of file diff --git a/v2/internal/binding/binding.go b/v2/internal/binding/binding.go new file mode 100755 index 000000000..ef33d858f --- /dev/null +++ b/v2/internal/binding/binding.go @@ -0,0 +1,88 @@ +package binding + +import ( + "fmt" + "github.com/leaanthony/typescriptify-golang-structs/typescriptify" + "reflect" + "runtime" + "strings" + + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/logger" +) + +type Bindings struct { + db *DB + logger logger.CustomLogger + exemptions slicer.StringSlicer + + // Typescript writer + converter *typescriptify.TypeScriptify +} + +// NewBindings returns a new Bindings object +func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings { + result := &Bindings{ + db: newDB(), + logger: logger.CustomLogger("Bindings"), + converter: typescriptify.New(), + } + + // No backups + result.converter.WithBackupDir("") + + // Hack for TS compilation error + result.converter.AddImport("export {};") + + for _, exemption := range exemptions { + if exemptions == nil { + continue + } + name := runtime.FuncForPC(reflect.ValueOf(exemption).Pointer()).Name() + // Yuk yuk yuk! Is there a better way? + name = strings.TrimSuffix(name, "-fm") + result.exemptions.Add(name) + } + + // Add the structs to bind + for _, ptr := range structPointersToBind { + err := result.Add(ptr) + if err != nil { + logger.Fatal("Error during binding: " + err.Error()) + } + } + + return result +} + +// Add the given struct methods to the Bindings +func (b *Bindings) Add(structPtr interface{}) error { + + methods, err := b.getMethods(structPtr) + if err != nil { + return fmt.Errorf("cannot bind value to app: %s", err.Error()) + } + + for _, method := range methods { + splitName := strings.Split(method.Name, ".") + packageName := splitName[0] + structName := splitName[1] + methodName := splitName[2] + + // Add it as a regular method + b.db.AddMethod(packageName, structName, methodName, method) + } + return nil +} + +func (b *Bindings) WriteTS(filename string) error { + return b.converter.ConvertToFile(filename) +} + +func (b *Bindings) DB() *DB { + return b.db +} + +func (b *Bindings) ToJSON() (string, error) { + return b.db.ToJSON() +} diff --git a/v2/internal/binding/boundMethod.go b/v2/internal/binding/boundMethod.go new file mode 100644 index 000000000..f6ffdb600 --- /dev/null +++ b/v2/internal/binding/boundMethod.go @@ -0,0 +1,100 @@ +package binding + +import ( + "encoding/json" + "fmt" + "reflect" +) + +// BoundMethod defines all the data related to a Go method that is +// bound to the Wails application +type BoundMethod struct { + Name string `json:"name"` + Inputs []*Parameter `json:"inputs,omitempty"` + Outputs []*Parameter `json:"outputs,omitempty"` + Comments string `json:"comments,omitempty"` + Method reflect.Value `json:"-"` +} + +// InputCount returns the number of inputs this bound method has +func (b *BoundMethod) InputCount() int { + return len(b.Inputs) +} + +// OutputCount returns the number of outputs this bound method has +func (b *BoundMethod) OutputCount() int { + return len(b.Outputs) +} + +// ParseArgs method converts the input json into the types expected by the method +func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) { + + result := make([]interface{}, b.InputCount()) + if len(args) != b.InputCount() { + return nil, fmt.Errorf("received %d arguments to method '%s', expected %d", len(args), b.Name, b.InputCount()) + } + for index, arg := range args { + typ := b.Inputs[index].reflectType + inputValue := reflect.New(typ).Interface() + err := json.Unmarshal(arg, inputValue) + if err != nil { + return nil, err + } + if inputValue == nil { + result[index] = reflect.Zero(typ).Interface() + } else { + result[index] = reflect.ValueOf(inputValue).Elem().Interface() + } + } + return result, nil +} + +// Call will attempt to call this bound method with the given args +func (b *BoundMethod) Call(args []interface{}) (interface{}, error) { + // Check inputs + expectedInputLength := len(b.Inputs) + actualInputLength := len(args) + if expectedInputLength != actualInputLength { + return nil, fmt.Errorf("%s takes %d inputs. Received %d", b.Name, expectedInputLength, actualInputLength) + } + + /** Convert inputs to reflect values **/ + + // Create slice for the input arguments to the method call + callArgs := make([]reflect.Value, expectedInputLength) + + // Iterate over given arguments + for index, arg := range args { + // Save the converted argument + callArgs[index] = reflect.ValueOf(arg) + } + + // Do the call + callResults := b.Method.Call(callArgs) + + //** Check results **// + var returnValue interface{} + var err error + + switch b.OutputCount() { + case 1: + // Loop over results and determine if the result + // is an error or not + for _, result := range callResults { + interfac := result.Interface() + temp, ok := interfac.(error) + if ok { + err = temp + } else { + returnValue = interfac + } + } + case 2: + returnValue = callResults[0].Interface() + if temp, ok := callResults[1].Interface().(error); ok { + err = temp + } + } + + return returnValue, err +} diff --git a/v2/internal/binding/db.go b/v2/internal/binding/db.go new file mode 100644 index 000000000..37c369720 --- /dev/null +++ b/v2/internal/binding/db.go @@ -0,0 +1,104 @@ +package binding + +import ( + "encoding/json" + "sync" + "unsafe" +) + +// DB is our database of method bindings +type DB struct { + // map[packagename] -> map[structname] -> map[methodname]*method + store map[string]map[string]map[string]*BoundMethod + + // This uses fully qualified method names as a shortcut for store traversal. + // It used for performance gains at runtime + methodMap map[string]*BoundMethod + + // Lock to ensure sync access to the data + lock sync.RWMutex +} + +func newDB() *DB { + return &DB{ + store: make(map[string]map[string]map[string]*BoundMethod), + methodMap: make(map[string]*BoundMethod), + } +} + +// GetMethodFromStore returns the method for the given package/struct/method names +// nil is returned if any one of those does not exist +func (d *DB) GetMethodFromStore(packageName string, structName string, methodName string) *BoundMethod { + + // Lock the db whilst processing and unlock on return + d.lock.RLock() + defer d.lock.RUnlock() + + structMap, exists := d.store[packageName] + if !exists { + return nil + } + methodMap, exists := structMap[structName] + if !exists { + return nil + } + return methodMap[methodName] +} + +// GetMethod returns the method for the given qualified method name +// qualifiedMethodName is "packagename.structname.methodname" +func (d *DB) GetMethod(qualifiedMethodName string) *BoundMethod { + + // Lock the db whilst processing and unlock on return + d.lock.RLock() + defer d.lock.RUnlock() + + return d.methodMap[qualifiedMethodName] +} + +// AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName +func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) { + + // TODO: Validate inputs? + + // Lock the db whilst processing and unlock on return + d.lock.Lock() + defer d.lock.Unlock() + + // Get the map associated with the package name + structMap, exists := d.store[packageName] + if !exists { + // Create a new map for this packagename + d.store[packageName] = make(map[string]map[string]*BoundMethod) + structMap = d.store[packageName] + } + + // Get the map associated with the struct name + methodMap, exists := structMap[structName] + if !exists { + // Create a new map for this packagename + structMap[structName] = make(map[string]*BoundMethod) + methodMap = structMap[structName] + } + + // Store the method definition + methodMap[methodName] = methodDefinition + + // Store in the methodMap + key := packageName + "." + structName + "." + methodName + d.methodMap[key] = methodDefinition + +} + +// ToJSON converts the method map to JSON +func (d *DB) ToJSON() (string, error) { + + // Lock the db whilst processing and unlock on return + d.lock.RLock() + defer d.lock.RUnlock() + + bytes, err := json.Marshal(&d.store) + + // Return zero copy string as this string will be read only + return *(*string)(unsafe.Pointer(&bytes)), err +} diff --git a/v2/internal/binding/generate.go b/v2/internal/binding/generate.go new file mode 100644 index 000000000..dd90f2ae0 --- /dev/null +++ b/v2/internal/binding/generate.go @@ -0,0 +1,149 @@ +package binding + +import ( + "bytes" + _ "embed" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/wailsapp/wails/v2/internal/fs" + + "github.com/leaanthony/slicer" +) + +//go:embed assets/package.json +var packageJSON []byte + +func (b *Bindings) GenerateBackendJS(targetfile string, isDevBindings bool) error { + + store := b.db.store + var output bytes.Buffer + + output.WriteString(`// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT +`) + + if isDevBindings { + json, err := b.ToJSON() + if err != nil { + return err + } + output.WriteString("window.wailsbindings = " + json + ";") + output.WriteString("\n") + } + + output.WriteString(`const go = {`) + output.WriteString("\n") + + var sortedPackageNames slicer.StringSlicer + for packageName := range store { + sortedPackageNames.Add(packageName) + } + sortedPackageNames.Sort() + sortedPackageNames.Each(func(packageName string) { + packages := store[packageName] + output.WriteString(fmt.Sprintf(" \"%s\": {", packageName)) + output.WriteString("\n") + var sortedStructNames slicer.StringSlicer + for structName := range packages { + sortedStructNames.Add(structName) + } + sortedStructNames.Sort() + + sortedStructNames.Each(func(structName string) { + structs := packages[structName] + output.WriteString(fmt.Sprintf(" \"%s\": {", structName)) + output.WriteString("\n") + + var sortedMethodNames slicer.StringSlicer + for methodName := range structs { + sortedMethodNames.Add(methodName) + } + sortedMethodNames.Sort() + + sortedMethodNames.Each(func(methodName string) { + methodDetails := structs[methodName] + output.WriteString(" /**\n") + output.WriteString(" * " + methodName + "\n") + var args slicer.StringSlicer + for count, input := range methodDetails.Inputs { + arg := fmt.Sprintf("arg%d", count+1) + args.Add(arg) + output.WriteString(fmt.Sprintf(" * @param {%s} %s - Go Type: %s\n", goTypeToJSDocType(input.TypeName), arg, input.TypeName)) + } + returnType := "Promise" + returnTypeDetails := "" + if methodDetails.OutputCount() > 0 { + firstType := goTypeToJSDocType(methodDetails.Outputs[0].TypeName) + returnType += "<" + firstType + if methodDetails.OutputCount() == 2 { + secondType := goTypeToJSDocType(methodDetails.Outputs[1].TypeName) + returnType += "|" + secondType + } + returnType += ">" + returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName + } else { + returnType = "Promise" + } + output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n") + output.WriteString(" */\n") + argsString := args.Join(", ") + output.WriteString(fmt.Sprintf(" \"%s\": (%s) => {", methodName, argsString)) + output.WriteString("\n") + output.WriteString(fmt.Sprintf(" return window.go.%s.%s.%s(%s);", packageName, structName, methodName, argsString)) + output.WriteString("\n") + output.WriteString(fmt.Sprintf(" },")) + output.WriteString("\n") + + }) + + output.WriteString(" },\n") + }) + + output.WriteString(" },\n\n") + }) + + output.WriteString(`}; +export default go;`) + output.WriteString("\n") + + dir := filepath.Dir(targetfile) + packageJsonFile := filepath.Join(dir, "package.json") + if !fs.FileExists(packageJsonFile) { + err := os.WriteFile(packageJsonFile, packageJSON, 0755) + if err != nil { + return err + } + } + + return os.WriteFile(targetfile, output.Bytes(), 0755) +} + +func goTypeToJSDocType(input string) string { + switch true { + case input == "string": + return "string" + case input == "error": + return "Error" + case + strings.HasPrefix(input, "int"), + strings.HasPrefix(input, "uint"), + strings.HasPrefix(input, "float"): + return "number" + case input == "bool": + return "boolean" + case input == "[]byte": + return "string" + case strings.HasPrefix(input, "[]"): + arrayType := goTypeToJSDocType(input[2:]) + return "Array.<" + arrayType + ">" + default: + if strings.ContainsRune(input, '.') { + return strings.Split(input, ".")[1] + } + return "any" + } +} diff --git a/v2/internal/binding/generate_test.go b/v2/internal/binding/generate_test.go new file mode 100644 index 000000000..e2751ceb5 --- /dev/null +++ b/v2/internal/binding/generate_test.go @@ -0,0 +1,87 @@ +package binding + +import ( + "testing" +) + +func Test_goTypeToJSDocType(t *testing.T) { + + tests := []struct { + name string + input string + want string + }{ + { + name: "string", + input: "string", + want: "string", + }, + { + name: "error", + input: "error", + want: "Error", + }, + { + name: "int", + input: "int", + want: "number", + }, + { + name: "int32", + input: "int32", + want: "number", + }, + { + name: "uint", + input: "uint", + want: "number", + }, + { + name: "uint32", + input: "uint32", + want: "number", + }, + { + name: "float32", + input: "float32", + want: "number", + }, + { + name: "float64", + input: "float64", + want: "number", + }, + { + name: "bool", + input: "bool", + want: "boolean", + }, + { + name: "[]byte", + input: "[]byte", + want: "string", + }, + { + name: "[]int", + input: "[]int", + want: "Array.", + }, + { + name: "[]bool", + input: "[]bool", + want: "Array.", + }, + { + name: "anything else", + input: "foo", + want: "any", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := goTypeToJSDocType(tt.input); got != tt.want { + t.Errorf("goTypeToJSDocType() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/v2/internal/binding/parameter.go b/v2/internal/binding/parameter.go new file mode 100644 index 000000000..ef10a24ec --- /dev/null +++ b/v2/internal/binding/parameter.go @@ -0,0 +1,28 @@ +package binding + +import "reflect" + +// Parameter defines a Go method parameter +type Parameter struct { + Name string `json:"name,omitempty"` + TypeName string `json:"type"` + reflectType reflect.Type +} + +func newParameter(Name string, Type reflect.Type) *Parameter { + return &Parameter{ + Name: Name, + TypeName: Type.String(), + reflectType: Type, + } +} + +// IsType returns true if the given +func (p *Parameter) IsType(typename string) bool { + return p.TypeName == typename +} + +// IsError returns true if the parameter type is an error +func (p *Parameter) IsError() bool { + return p.IsType("error") +} diff --git a/v2/internal/binding/reflect.go b/v2/internal/binding/reflect.go new file mode 100755 index 000000000..3b79428f0 --- /dev/null +++ b/v2/internal/binding/reflect.go @@ -0,0 +1,120 @@ +package binding + +import ( + "fmt" + "reflect" + "runtime" +) + +// isStructPtr returns true if the value given is a +// pointer to a struct +func isStructPtr(value interface{}) bool { + return reflect.ValueOf(value).Kind() == reflect.Ptr && + reflect.ValueOf(value).Elem().Kind() == reflect.Struct +} + +// isFunction returns true if the given value is a function +func isFunction(value interface{}) bool { + return reflect.ValueOf(value).Kind() == reflect.Func +} + +// isStructPtr returns true if the value given is a struct +func isStruct(value interface{}) bool { + return reflect.ValueOf(value).Kind() == reflect.Struct +} + +func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) { + + // Create result placeholder + var result []*BoundMethod + + // Check type + if !isStructPtr(value) { + + if isStruct(value) { + name := reflect.ValueOf(value).Type().Name() + return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name) + } + + if isFunction(value) { + name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name() + return nil, fmt.Errorf("%s is a function, not a pointer to a struct. Wails v2 has deprecated the binding of functions. Please wrap your functions up in a struct and bind a pointer to that struct.", name) + } + + return nil, fmt.Errorf("not a pointer to a struct.") + } + + // Process Struct + structType := reflect.TypeOf(value) + structValue := reflect.ValueOf(value) + baseName := structType.String()[1:] + + // Process Methods + for i := 0; i < structType.NumMethod(); i++ { + methodDef := structType.Method(i) + methodName := methodDef.Name + fullMethodName := baseName + "." + methodName + method := structValue.MethodByName(methodName) + + methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name() + if b.exemptions.Contains(methodReflectName) { + continue + } + + // Create new method + boundMethod := &BoundMethod{ + Name: fullMethodName, + Inputs: nil, + Outputs: nil, + Comments: "", + Method: method, + } + + // Iterate inputs + methodType := method.Type() + inputParamCount := methodType.NumIn() + var inputs []*Parameter + for inputIndex := 0; inputIndex < inputParamCount; inputIndex++ { + input := methodType.In(inputIndex) + thisParam := newParameter("", input) + + // Process struct pointer params + if input.Kind() == reflect.Ptr { + if input.Elem().Kind() == reflect.Struct { + typ := input.Elem() + a := reflect.New(typ) + s := reflect.Indirect(a).Interface() + b.converter.Add(s) + } + } + + // Process struct params + if input.Kind() == reflect.Struct { + a := reflect.New(input) + s := reflect.Indirect(a).Interface() + b.converter.Add(s) + } + + inputs = append(inputs, thisParam) + } + + boundMethod.Inputs = inputs + + // Iterate outputs + // TODO: Determine what to do about limiting return types + // especially around errors. + outputParamCount := methodType.NumOut() + var outputs []*Parameter + for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ { + output := methodType.Out(outputIndex) + thisParam := newParameter("", output) + outputs = append(outputs, thisParam) + } + boundMethod.Outputs = outputs + + // Save method in result + result = append(result, boundMethod) + + } + return result, nil +} diff --git a/v2/internal/bridge/bridge.go b/v2/internal/bridge/bridge.go new file mode 100644 index 000000000..1ea509d33 --- /dev/null +++ b/v2/internal/bridge/bridge.go @@ -0,0 +1,113 @@ +package bridge + +import ( + "context" + "log" + "net/http" + "sync" + + "github.com/wailsapp/wails/v2/internal/menumanager" + + "github.com/wailsapp/wails/v2/internal/messagedispatcher" + + "github.com/gorilla/websocket" + "github.com/wailsapp/wails/v2/internal/logger" +) + +type Bridge struct { + upgrader websocket.Upgrader + server *http.Server + myLogger *logger.Logger + + bindings string + dispatcher *messagedispatcher.Dispatcher + + mu sync.Mutex + sessions map[string]*session + + ctx context.Context + cancel context.CancelFunc + + // Dialog client + dialog *messagedispatcher.DispatchClient + + // Menus + menumanager *menumanager.Manager +} + +func NewBridge(myLogger *logger.Logger) *Bridge { + result := &Bridge{ + myLogger: myLogger, + upgrader: websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}, + sessions: make(map[string]*session), + } + + myLogger.SetLogLevel(1) + + ctx, cancel := context.WithCancel(context.Background()) + result.ctx = ctx + result.cancel = cancel + result.server = &http.Server{Addr: ":34115"} + http.HandleFunc("/bridge", result.wsBridgeHandler) + return result +} + +func (b *Bridge) Run(dispatcher *messagedispatcher.Dispatcher, menumanager *menumanager.Manager, bindings string, debug bool) error { + + // Ensure we cancel the context when we shutdown + defer b.cancel() + + b.bindings = bindings + b.dispatcher = dispatcher + b.menumanager = menumanager + + // Setup dialog handler + dialogClient := NewDialogClient(b.myLogger) + b.dialog = dispatcher.RegisterClient(dialogClient) + dialogClient.dispatcher = b.dialog + + b.myLogger.Info("Bridge mode started.") + + err := b.server.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + return err + } + + return nil +} + +func (b *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) { + c, err := b.upgrader.Upgrade(w, r, nil) + if err != nil { + log.Print("upgrade:", err) + return + } + + if err != nil { + http.Error(w, "Could not open websocket connection", http.StatusBadRequest) + } + b.myLogger.Info("Connection from frontend accepted [%s].", c.RemoteAddr().String()) + b.startSession(c) + +} + +func (b *Bridge) startSession(conn *websocket.Conn) { + + // Create a new session for this connection + s := newSession(conn, b.menumanager, b.bindings, b.dispatcher, b.myLogger, b.ctx) + + // Setup the close handler + conn.SetCloseHandler(func(int, string) error { + b.myLogger.Info("Connection dropped [%s].", s.Identifier()) + b.dispatcher.RemoveClient(s.client) + b.mu.Lock() + delete(b.sessions, s.Identifier()) + b.mu.Unlock() + return nil + }) + + b.mu.Lock() + go s.start(len(b.sessions) == 0) + b.sessions[s.Identifier()] = s + b.mu.Unlock() +} diff --git a/v2/internal/bridge/client.go b/v2/internal/bridge/client.go new file mode 100644 index 000000000..f65cffc20 --- /dev/null +++ b/v2/internal/bridge/client.go @@ -0,0 +1,141 @@ +package bridge + +import ( + "github.com/wailsapp/wails/v2/pkg/runtime" +) + +type BridgeClient struct { + session *session + + // Tray menu cache to send to reconnecting clients + messageCache chan string +} + +func (b BridgeClient) DeleteTrayMenuByID(id string) { + b.session.sendMessage("TD" + id) +} + +func NewBridgeClient() *BridgeClient { + return &BridgeClient{ + messageCache: make(chan string, 100), + } +} + +func (b BridgeClient) Quit() { + b.session.log.Info("Quit unsupported in Bridge mode") +} + +func (b BridgeClient) NotifyEvent(message string) { + b.session.sendMessage("n" + message) + b.session.log.Info("Notify: %s", message) +} + +func (b BridgeClient) CallResult(message string) { + b.session.sendMessage("c" + message) +} + +func (b BridgeClient) OpenFileDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + // Handled by dialog_client +} + +func (b BridgeClient) OpenMultipleFilesDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + // Handled by dialog_client +} + +func (b BridgeClient) OpenDirectoryDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + // Handled by dialog_client +} + +func (b BridgeClient) SaveDialog(dialogOptions runtime.SaveDialogOptions, callbackID string) { + // Handled by dialog_client +} + +func (b BridgeClient) MessageDialog(dialogOptions runtime.MessageDialogOptions, callbackID string) { + // Handled by dialog_client +} + +func (b BridgeClient) WindowSetTitle(title string) { + b.session.log.Info("WindowSetTitle unsupported in Bridge mode") +} + +func (b BridgeClient) WindowShow() { + b.session.log.Info("WindowShow unsupported in Bridge mode") +} + +func (b BridgeClient) WindowHide() { + b.session.log.Info("WindowHide unsupported in Bridge mode") +} + +func (b BridgeClient) WindowCenter() { + b.session.log.Info("WindowCenter unsupported in Bridge mode") +} + +func (b BridgeClient) WindowMaximise() { + b.session.log.Info("WindowMaximise unsupported in Bridge mode") +} + +func (b BridgeClient) WindowUnmaximise() { + b.session.log.Info("WindowUnmaximise unsupported in Bridge mode") +} + +func (b BridgeClient) WindowMinimise() { + b.session.log.Info("WindowMinimise unsupported in Bridge mode") +} + +func (b BridgeClient) WindowUnminimise() { + b.session.log.Info("WindowUnminimise unsupported in Bridge mode") +} + +func (b BridgeClient) WindowPosition(x int, y int) { + b.session.log.Info("WindowPosition unsupported in Bridge mode") +} + +func (b BridgeClient) WindowSize(width int, height int) { + b.session.log.Info("WindowSize unsupported in Bridge mode") +} + +func (b BridgeClient) WindowSetMinSize(width int, height int) { + b.session.log.Info("WindowSetMinSize unsupported in Bridge mode") +} + +func (b BridgeClient) WindowSetMaxSize(width int, height int) { + b.session.log.Info("WindowSetMaxSize unsupported in Bridge mode") +} + +func (b BridgeClient) WindowFullscreen() { + b.session.log.Info("WindowFullscreen unsupported in Bridge mode") +} + +func (b BridgeClient) WindowUnFullscreen() { + b.session.log.Info("WindowUnFullscreen unsupported in Bridge mode") +} + +func (b BridgeClient) WindowSetColour(colour int) { + b.session.log.Info("WindowSetColour unsupported in Bridge mode") +} + +func (b BridgeClient) DarkModeEnabled(callbackID string) { + b.session.log.Info("DarkModeEnabled unsupported in Bridge mode") +} + +func (b BridgeClient) MenuSetApplicationMenu(menuJSON string) { + b.session.log.Info("MenuSetApplicationMenu unsupported in Bridge mode") +} + +func (b BridgeClient) SetTrayMenu(trayMenuJSON string) { + b.session.sendMessage("TS" + trayMenuJSON) +} + +func (b BridgeClient) UpdateTrayMenuLabel(trayMenuJSON string) { + b.session.sendMessage("TU" + trayMenuJSON) +} + +func (b BridgeClient) UpdateContextMenu(contextMenuJSON string) { + b.session.log.Info("UpdateContextMenu unsupported in Bridge mode") +} + +func newBridgeClient(session *session) *BridgeClient { + return &BridgeClient{ + session: session, + } +} diff --git a/v2/internal/bridge/darwin.js b/v2/internal/bridge/darwin.js new file mode 100644 index 000000000..4758b4e29 --- /dev/null +++ b/v2/internal/bridge/darwin.js @@ -0,0 +1 @@ +var Wails=function(n){var t={};function e(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return n[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}return e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var i in n)e.d(r,i,function(t){return n[t]}.bind(null,i));return r},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=0)}([function(n,t,e){"use strict";e.r(t);var r={};e.r(r),e.d(r,"Trace",(function(){return v})),e.d(r,"Print",(function(){return p})),e.d(r,"Debug",(function(){return y})),e.d(r,"Info",(function(){return m})),e.d(r,"Warning",(function(){return b})),e.d(r,"Error",(function(){return g})),e.d(r,"Fatal",(function(){return h})),e.d(r,"SetLogLevel",(function(){return S})),e.d(r,"Level",(function(){return E}));var i={};e.r(i),e.d(i,"Open",(function(){return x}));var o={};e.r(o),e.d(o,"Center",(function(){return N})),e.d(o,"SetTitle",(function(){return T})),e.d(o,"Fullscreen",(function(){return j})),e.d(o,"UnFullscreen",(function(){return D})),e.d(o,"SetSize",(function(){return I})),e.d(o,"SetPosition",(function(){return P})),e.d(o,"Hide",(function(){return A})),e.d(o,"Show",(function(){return J})),e.d(o,"Maximise",(function(){return L})),e.d(o,"Unmaximise",(function(){return R})),e.d(o,"Minimise",(function(){return _})),e.d(o,"Unminimise",(function(){return F})),e.d(o,"Close",(function(){return U}));var a={};e.r(a),e.d(a,"Open",(function(){return B})),e.d(a,"Save",(function(){return H})),e.d(a,"Message",(function(){return G}));var u={};e.r(u),e.d(u,"New",(function(){return en}));var c={};e.r(c),e.d(c,"SetIcon",(function(){return rn}));var l={AppType:"desktop",Platform:function(){return"darwin"}};var s=[];function f(n){s.push(n)}function d(n){if(function(n){window.wailsInvoke(n)}(n),s.length>0)for(var t=0;t0)var a=setTimeout((function(){i(Error("Call to "+n+" timed out. Request ID: "+o))}),e);k[o]={timeoutHandle:a,reject:i,resolve:r};try{var u={name:n,args:t,callbackID:o};d("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function W(n){var t;try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw y(e),new Error(e)}var r=t.callbackid,i=k[r];if(!i){var o="Callback '".concat(r,"' not registered!!!");throw console.error(o),new Error(o)}clearTimeout(i.timeoutHandle),delete k[r],t.error?i.reject(t.error):i.resolve(t.result)}function M(n){var t=[].slice.apply(arguments).slice(1);return C(".wails."+n,t)}function x(n){return d("RBO"+n)}function N(){d("Wc")}function T(n){d("WT"+n)}function j(){d("WF")}function D(){d("Wf")}function I(n,t){d("Ws:"+n+":"+t)}function P(n,t){d("Wp:"+n+":"+t)}function A(){d("WH")}function J(){d("WS")}function L(){d("WM")}function R(){d("WU")}function _(){d("Wm")}function F(){d("Wu")}function U(){d("WC")}function B(n){return M("Dialog.Open",n)}function H(n){return M("Dialog.Save",n)}function G(n){return M("Dialog.Message",n)}O=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var q=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},z={};function V(n,t,e){z[n]=z[n]||[];var r=new q(t,e);console.log("Pushing event listener: "+n),z[n].push(r)}function K(n,t){V(n,t)}function Q(n,t){V(n,t,1)}function X(n){var t=n.name;if(z[t]){for(var e=z[t].slice(),r=0;r0)for(var t=0;t0)var a=setTimeout((function(){i(Error("Call to "+n+" timed out. Request ID: "+o))}),e);k[o]={timeoutHandle:a,reject:i,resolve:r};try{var u={name:n,args:t,callbackID:o};d("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function W(n){var t;try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw y(e),new Error(e)}var r=t.callbackid,i=k[r];if(!i){var o="Callback '".concat(r,"' not registered!!!");throw console.error(o),new Error(o)}clearTimeout(i.timeoutHandle),delete k[r],t.error?i.reject(t.error):i.resolve(t.result)}function x(n){var t=[].slice.apply(arguments).slice(1);return C(".wails."+n,t)}function M(n){return d("RBO"+n)}function N(){d("Wc")}function T(n){d("WT"+n)}function j(){d("WF")}function D(){d("Wf")}function I(n,t){d("Ws:"+n+":"+t)}function P(n,t){d("Wp:"+n+":"+t)}function A(){d("WH")}function J(){d("WS")}function L(){d("WM")}function R(){d("WU")}function _(){d("Wm")}function F(){d("Wu")}function U(){d("WC")}function B(n){return x("Dialog.Open",n)}function H(n){return x("Dialog.Save",n)}function G(n){return x("Dialog.Message",n)}O=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var q=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},z={};function V(n,t,e){z[n]=z[n]||[];var r=new q(t,e);console.log("Pushing event listener: "+n),z[n].push(r)}function K(n,t){V(n,t)}function Q(n,t){V(n,t,1)}function X(n){var t=n.name;if(z[t]){for(var e=z[t].slice(),r=0;r:. +func (s *session) Identifier() string { + if s.conn != nil { + return s.conn.RemoteAddr().String() + } + return "" +} + +func (s *session) sendMessage(msg string) error { + if !s.done { + s.writeChan <- []byte(msg) + } + return nil +} + +func (s *session) start(firstSession bool) { + s.log.SetLogLevel(1) + s.log.Info("Connected to frontend.") + go s.writePump() + + var wailsRuntime string + switch runtime.GOOS { + case "darwin": + wailsRuntime = darwinRuntime + case "windows": + wailsRuntime = windowsRuntime + case "linux": + wailsRuntime = linuxRuntime + default: + log.Fatal("platform not supported") + } + + bindingsMessage := "window.wailsbindings = `" + s.bindings + "`;" + s.log.Info(bindingsMessage) + bootstrapMessage := bindingsMessage + wailsRuntime + + err := s.sendMessage("b" + bootstrapMessage) + if err != nil { + s.log.Error(err.Error()) + } + + // Send menus + traymenus, err := s.menumanager.GetTrayMenus() + if err != nil { + s.log.Error(err.Error()) + } + + for _, trayMenu := range traymenus { + err := s.sendMessage("TS" + trayMenu) + if err != nil { + s.log.Error(err.Error()) + } + } + + for { + messageType, buffer, err := s.conn.ReadMessage() + if messageType == -1 { + return + } + if err != nil { + s.log.Error("Error reading message: %v", err) + err = s.conn.Close() + return + } + + message := string(buffer) + + s.log.Debug("Got message: %#v\n", message) + + // Dispatch message as normal + s.client.DispatchMessage(message) + + if s.done { + break + } + } +} + +// Shutdown +func (s *session) Shutdown() { + err := s.conn.Close() + if err != nil { + s.log.Error(err.Error()) + } + s.done = true + s.log.Info("session %v exit", s.Identifier()) +} + +// writePump pulls messages from the writeChan and sends them to the client +// since it uses a channel to read the messages the socket is protected without locks +func (s *session) writePump() { + s.log.Debug("Session %v - writePump start", s.Identifier()) + defer s.log.Debug("Session %v - writePump shutdown", s.Identifier()) + for { + select { + case <-s.ctx.Done(): + s.Shutdown() + return + case msg, ok := <-s.writeChan: + err := s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second)) + if err != nil { + s.log.Error(err.Error()) + } + if !ok { + s.log.Debug("writeChan was closed!") + err := s.conn.WriteMessage(websocket.CloseMessage, []byte{}) + if err != nil { + s.log.Error(err.Error()) + } + return + } + + if err := s.conn.WriteMessage(websocket.TextMessage, msg); err != nil { + s.log.Debug(err.Error()) + return + } + } + } +} diff --git a/v2/internal/bridge/windows.js b/v2/internal/bridge/windows.js new file mode 100644 index 000000000..281f47bf1 --- /dev/null +++ b/v2/internal/bridge/windows.js @@ -0,0 +1 @@ +var Wails=function(n){var t={};function e(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return n[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}return e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var i in n)e.d(r,i,function(t){return n[t]}.bind(null,i));return r},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=0)}([function(n,t,e){"use strict";e.r(t);var r={};e.r(r),e.d(r,"Trace",(function(){return v})),e.d(r,"Print",(function(){return p})),e.d(r,"Debug",(function(){return y})),e.d(r,"Info",(function(){return m})),e.d(r,"Warning",(function(){return b})),e.d(r,"Error",(function(){return g})),e.d(r,"Fatal",(function(){return h})),e.d(r,"SetLogLevel",(function(){return S})),e.d(r,"Level",(function(){return E}));var i={};e.r(i),e.d(i,"Open",(function(){return T}));var o={};e.r(o),e.d(o,"Center",(function(){return j})),e.d(o,"SetTitle",(function(){return x})),e.d(o,"Fullscreen",(function(){return M})),e.d(o,"UnFullscreen",(function(){return I})),e.d(o,"SetSize",(function(){return D})),e.d(o,"SetPosition",(function(){return P})),e.d(o,"Hide",(function(){return A})),e.d(o,"Show",(function(){return J})),e.d(o,"Maximise",(function(){return L})),e.d(o,"Unmaximise",(function(){return R})),e.d(o,"Minimise",(function(){return _})),e.d(o,"Unminimise",(function(){return F})),e.d(o,"Close",(function(){return U}));var a={};e.r(a),e.d(a,"Open",(function(){return B})),e.d(a,"Save",(function(){return H})),e.d(a,"Message",(function(){return G}));var u={};e.r(u),e.d(u,"New",(function(){return en}));var c={};e.r(c),e.d(c,"SetIcon",(function(){return rn}));var l={AppType:"desktop",Platform:function(){return"windows"}};var s=[];function f(n){s.push(n)}function d(n){if(function(n){window.wailsInvoke(n)}(n),s.length>0)for(var t=0;t0)var a=setTimeout((function(){i(Error("Call to "+n+" timed out. Request ID: "+o))}),e);k[o]={timeoutHandle:a,reject:i,resolve:r};try{var u={name:n,args:t,callbackID:o};d("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function W(n){var t;try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw y(e),new Error(e)}var r=t.callbackid,i=k[r];if(!i){var o="Callback '".concat(r,"' not registered!!!");throw console.error(o),new Error(o)}clearTimeout(i.timeoutHandle),delete k[r],t.error?i.reject(t.error):i.resolve(t.result)}function N(n){var t=[].slice.apply(arguments).slice(1);return C(".wails."+n,t)}function T(n){return d("RBO"+n)}function j(){d("Wc")}function x(n){d("WT"+n)}function M(){d("WF")}function I(){d("Wf")}function D(n,t){d("Ws:"+n+":"+t)}function P(n,t){d("Wp:"+n+":"+t)}function A(){d("WH")}function J(){d("WS")}function L(){d("WM")}function R(){d("WU")}function _(){d("Wm")}function F(){d("Wu")}function U(){d("WC")}function B(n){return N("Dialog.Open",n)}function H(n){return N("Dialog.Save",n)}function G(n){return N("Dialog.Message",n)}O=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var q=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},z={};function V(n,t,e){z[n]=z[n]||[];var r=new q(t,e);console.log("Pushing event listener: "+n),z[n].push(r)}function K(n,t){V(n,t)}function Q(n,t){V(n,t,1)}function X(n){var t=n.name;if(z[t]){for(var e=z[t].slice(),r=0;rdata); + return -1; +} + +const char* getJSONString(JsonNode *item, const char* key) { + // Get key + JsonNode *node = json_find_member(item, key); + const char *result = ""; + if ( node != NULL && node->tag == JSON_STRING) { + result = node->string_; + } + return result; +} + +void ABORT_JSON(JsonNode *node, const char* key) { + ABORT("Unable to read required key '%s' from JSON: %s\n", key, json_encode(node)); +} + +const char* mustJSONString(JsonNode *node, const char* key) { + const char* result = getJSONString(node, key); + if ( result == NULL ) { + ABORT_JSON(node, key); + } + return result; +} +JsonNode* mustJSONObject(JsonNode *node, const char* key) { + struct JsonNode* result = getJSONObject(node, key); + if ( result == NULL ) { + ABORT_JSON(node, key); + } + return result; +} + +JsonNode* getJSONObject(JsonNode* node, const char* key) { + return json_find_member(node, key); +} + +bool getJSONBool(JsonNode *item, const char* key, bool *result) { + JsonNode *node = json_find_member(item, key); + if ( node != NULL && node->tag == JSON_BOOL) { + *result = node->bool_; + return true; + } + return false; +} + +bool getJSONInt(JsonNode *item, const char* key, int *result) { + JsonNode *node = json_find_member(item, key); + if ( node != NULL && node->tag == JSON_NUMBER) { + *result = (int) node->number_; + return true; + } + return false; +} + +JsonNode* mustParseJSON(const char* JSON) { + JsonNode* parsedUpdate = json_decode(JSON); + if ( parsedUpdate == NULL ) { + ABORT("Unable to decode JSON: %s\n", JSON); + } + return parsedUpdate; +} \ No newline at end of file diff --git a/v2/internal/ffenestri/common.h b/v2/internal/ffenestri/common.h new file mode 100644 index 000000000..7f6e3a509 --- /dev/null +++ b/v2/internal/ffenestri/common.h @@ -0,0 +1,36 @@ +// +// Created by Lea Anthony on 6/1/21. +// + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include "string.h" +#include "hashmap.h" +#include "vec.h" +#include "json.h" + +#define STREQ(a,b) strcmp(a, b) == 0 +#define STREMPTY(string) strlen(string) == 0 +#define STRCOPY(a) concat(a, "") +#define STR_HAS_CHARS(input) input != NULL && strlen(input) > 0 +#define MEMFREE(input) free((void*)input); input = NULL; +#define FREE_AND_SET(variable, value) if( variable != NULL ) { MEMFREE(variable); } variable = value + +// Credit: https://stackoverflow.com/a/8465083 +char* concat(const char *string1, const char *string2); +void ABORT(const char *message, ...); +int freeHashmapItem(void *const context, struct hashmap_element_s *const e); +const char* getJSONString(JsonNode *item, const char* key); +const char* mustJSONString(JsonNode *node, const char* key); +JsonNode* getJSONObject(JsonNode* node, const char* key); +JsonNode* mustJSONObject(JsonNode *node, const char* key); + +bool getJSONBool(JsonNode *item, const char* key, bool *result); +bool getJSONInt(JsonNode *item, const char* key, int *result); + +JsonNode* mustParseJSON(const char* JSON); + +#endif //ASSETS_C_COMMON_H diff --git a/v2/internal/ffenestri/contextmenus_darwin.c b/v2/internal/ffenestri/contextmenus_darwin.c new file mode 100644 index 000000000..fb2eb4105 --- /dev/null +++ b/v2/internal/ffenestri/contextmenus_darwin.c @@ -0,0 +1,99 @@ +//// +//// Created by Lea Anthony on 6/1/21. +//// +// + +#include "ffenestri_darwin.h" +#include "common.h" +#include "contextmenus_darwin.h" +#include "menu_darwin.h" + +ContextMenu* NewContextMenu(const char* contextMenuJSON) { + ContextMenu* result = malloc(sizeof(ContextMenu)); + + JsonNode* processedJSON = json_decode(contextMenuJSON); + if( processedJSON == NULL ) { + ABORT("[NewTrayMenu] Unable to parse TrayMenu JSON: %s", contextMenuJSON); + } + // Save reference to this json + result->processedJSON = processedJSON; + + result->ID = mustJSONString(processedJSON, "ID"); + JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu"); + + result->menu = NewMenu(processedMenu); + result->nsmenu = NULL; + result->menu->menuType = ContextMenuType; + result->menu->parentData = result; + result->contextMenuData = NULL; + return result; +} + +ContextMenu* GetContextMenuByID(ContextMenuStore* store, const char *contextMenuID) { + return (ContextMenu*)hashmap_get(&store->contextMenuMap, (char*)contextMenuID, strlen(contextMenuID)); +} + +void DeleteContextMenu(ContextMenu* contextMenu) { + // Free Menu + DeleteMenu(contextMenu->menu); + + // Delete any context menu data we may have stored + if( contextMenu->contextMenuData != NULL ) { + MEMFREE(contextMenu->contextMenuData); + } + + // Free JSON + if (contextMenu->processedJSON != NULL ) { + json_delete(contextMenu->processedJSON); + contextMenu->processedJSON = NULL; + } + + // Free context menu + free(contextMenu); +} + +int freeContextMenu(void *const context, struct hashmap_element_s *const e) { + DeleteContextMenu(e->data); + return -1; +} + +void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *contextMenuID, const char *contextMenuData) { + + // If no context menu ID was given, abort + if( contextMenuID == NULL ) { + return; + } + + ContextMenu* contextMenu = GetContextMenuByID(store, contextMenuID); + + // We don't need the ID now + MEMFREE(contextMenuID); + + if( contextMenu == NULL ) { + // Free context menu data + if( contextMenuData != NULL ) { + MEMFREE(contextMenuData); + return; + } + } + + // We need to store the context menu data. Free existing data if we have it + // and set to the new value. + FREE_AND_SET(contextMenu->contextMenuData, contextMenuData); + + // Grab the content view and show the menu + id contentView = msg_reg(mainWindow, s("contentView")); + + // Get the triggering event + id menuEvent = msg_reg(mainWindow, s("currentEvent")); + + if( contextMenu->nsmenu == NULL ) { + // GetMenu creates the NSMenu + contextMenu->nsmenu = GetMenu(contextMenu->menu); + } + + // Show popup + ((id(*)(id, SEL, id, id, id))objc_msgSend)(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView); + +} + diff --git a/v2/internal/ffenestri/contextmenus_darwin.h b/v2/internal/ffenestri/contextmenus_darwin.h new file mode 100644 index 000000000..a1e7b976a --- /dev/null +++ b/v2/internal/ffenestri/contextmenus_darwin.h @@ -0,0 +1,33 @@ +//// +//// Created by Lea Anthony on 6/1/21. +//// +// +#ifndef CONTEXTMENU_DARWIN_H +#define CONTEXTMENU_DARWIN_H + +#include "json.h" +#include "menu_darwin.h" +#include "contextmenustore_darwin.h" + +typedef struct { + const char* ID; + id nsmenu; + Menu* menu; + + JsonNode* processedJSON; + + // Context menu data is given by the frontend when clicking a context menu. + // We send this to the backend when an item is selected + const char* contextMenuData; +} ContextMenu; + + +ContextMenu* NewContextMenu(const char* contextMenuJSON); + +ContextMenu* GetContextMenuByID( ContextMenuStore* store, const char *contextMenuID); +void DeleteContextMenu(ContextMenu* contextMenu); +int freeContextMenu(void *const context, struct hashmap_element_s *const e); + +void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *contextMenuID, const char *contextMenuData); + +#endif //CONTEXTMENU_DARWIN_H diff --git a/v2/internal/ffenestri/contextmenustore_darwin.c b/v2/internal/ffenestri/contextmenustore_darwin.c new file mode 100644 index 000000000..c777f014e --- /dev/null +++ b/v2/internal/ffenestri/contextmenustore_darwin.c @@ -0,0 +1,65 @@ + +#include "contextmenus_darwin.h" +#include "contextmenustore_darwin.h" + +ContextMenuStore* NewContextMenuStore() { + + ContextMenuStore* result = malloc(sizeof(ContextMenuStore)); + + // Allocate Context Menu Store + if( 0 != hashmap_create((const unsigned)4, &result->contextMenuMap)) { + ABORT("[NewContextMenus] Not enough memory to allocate contextMenuStore!"); + } + + return result; +} + +void AddContextMenuToStore(ContextMenuStore* store, const char* contextMenuJSON) { + ContextMenu* newMenu = NewContextMenu(contextMenuJSON); + + //TODO: check if there is already an entry for this menu + hashmap_put(&store->contextMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu); +} + +ContextMenu* GetContextMenuFromStore(ContextMenuStore* store, const char* menuID) { + // Get the current menu + return hashmap_get(&store->contextMenuMap, menuID, strlen(menuID)); +} + +void UpdateContextMenuInStore(ContextMenuStore* store, const char* menuJSON) { + ContextMenu* newContextMenu = NewContextMenu(menuJSON); + + // Get the current menu + ContextMenu *currentMenu = GetContextMenuFromStore(store, newContextMenu->ID); + if ( currentMenu == NULL ) { + ABORT("Attempted to update unknown context menu with ID '%s'.", newContextMenu->ID); + } + + hashmap_remove(&store->contextMenuMap, newContextMenu->ID, strlen(newContextMenu->ID)); + + // Save the status bar reference + DeleteContextMenu(currentMenu); + + hashmap_put(&store->contextMenuMap, newContextMenu->ID, strlen(newContextMenu->ID), newContextMenu); + +} + + +void DeleteContextMenuStore(ContextMenuStore* store) { + + // Guard against NULLs + if( store == NULL ) { + return; + } + + // Delete context menus + if( hashmap_num_entries(&store->contextMenuMap) > 0 ) { + if (0 != hashmap_iterate_pairs(&store->contextMenuMap, freeContextMenu, NULL)) { + ABORT("[DeleteContextMenuStore] Failed to release contextMenuStore entries!"); + } + } + + // Free context menu hashmap + hashmap_destroy(&store->contextMenuMap); + +} diff --git a/v2/internal/ffenestri/contextmenustore_darwin.h b/v2/internal/ffenestri/contextmenustore_darwin.h new file mode 100644 index 000000000..793eef691 --- /dev/null +++ b/v2/internal/ffenestri/contextmenustore_darwin.h @@ -0,0 +1,27 @@ +// +// Created by Lea Anthony on 7/1/21. +// + +#ifndef CONTEXTMENUSTORE_DARWIN_H +#define CONTEXTMENUSTORE_DARWIN_H + +#include "common.h" + +typedef struct { + + int dummy; + + // This is our context menu store which keeps track + // of all instances of ContextMenus + struct hashmap_s contextMenuMap; + +} ContextMenuStore; + +ContextMenuStore* NewContextMenuStore(); + +void DeleteContextMenuStore(ContextMenuStore* store); +void UpdateContextMenuInStore(ContextMenuStore* store, const char* menuJSON); + +void AddContextMenuToStore(ContextMenuStore* store, const char* contextMenuJSON); + +#endif //CONTEXTMENUSTORE_DARWIN_H diff --git a/v2/internal/ffenestri/defaultdialogicons_darwin.c b/v2/internal/ffenestri/defaultdialogicons_darwin.c new file mode 100644 index 000000000..0c79a6b55 --- /dev/null +++ b/v2/internal/ffenestri/defaultdialogicons_darwin.c @@ -0,0 +1,41 @@ +// defaultdialogicons_darwin.c +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. +// This file was auto-generated. DO NOT MODIFY. + +const unsigned char defaultDialogIcon0Name[] = { 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x64, 0x61, 0x72, 0x6b, 0x00 }; +const unsigned char defaultDialogIcon0Length[] = { 0x37, 0x38, 0x30, 0x00 }; +const unsigned char defaultDialogIcon0Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x9d, 0xb7, 0x81, 0xec, 0x0, 0x0, 0x0, 0x84, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0xdb, 0xe6, 0xd1, 0x0, 0x0, 0x0, 0x2b, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xf9, 0x9c, 0xb1, 0xe7, 0x4b, 0x91, 0x3a, 0x1b, 0xe3, 0x1e, 0xa, 0xae, 0x51, 0xe0, 0x98, 0x32, 0x4, 0x66, 0x6d, 0x2b, 0x63, 0x5d, 0x18, 0x11, 0xd6, 0xd0, 0xc8, 0xc2, 0x40, 0x7, 0x76, 0x16, 0x48, 0xa0, 0x2f, 0x57, 0xf1, 0xf0, 0x85, 0x77, 0x2c, 0xf, 0x6d, 0x84, 0xfd, 0x3d, 0x0, 0x0, 0x2, 0xc, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x57, 0xd9, 0x96, 0x82, 0x30, 0xc, 0x15, 0x2c, 0x4b, 0x91, 0x5d, 0x40, 0x10, 0x5, 0xf7, 0x65, 0xfc, 0xff, 0xff, 0x9b, 0x63, 0xca, 0xa1, 0x40, 0xb, 0x61, 0xa6, 0xc7, 0x37, 0xf3, 0x18, 0x92, 0xdb, 0x66, 0xb9, 0x69, 0x58, 0x7c, 0xe5, 0xb3, 0x62, 0x87, 0x46, 0xfa, 0x78, 0xa4, 0x46, 0x68, 0xff, 0xc3, 0x79, 0xe3, 0x99, 0x25, 0xd5, 0x5e, 0x20, 0x1a, 0x2d, 0x4d, 0x6f, 0xf3, 0x27, 0x77, 0x2b, 0xf0, 0x5f, 0x3, 0xf1, 0x3, 0x6b, 0xb6, 0xfb, 0x7e, 0xa9, 0xbd, 0x24, 0xa2, 0x2d, 0xf7, 0xb3, 0xdc, 0x1d, 0x1d, 0xdc, 0xa5, 0x10, 0xba, 0x83, 0xfb, 0x1b, 0x7e, 0xd7, 0x83, 0x14, 0x5, 0xe9, 0xe2, 0xf9, 0x6, 0xe6, 0xbf, 0x6e, 0xcd, 0xa9, 0x1b, 0x5b, 0x89, 0x93, 0xe7, 0x4e, 0x62, 0xc5, 0x2e, 0x6d, 0x21, 0xd7, 0x93, 0xee, 0x2b, 0xb3, 0x8d, 0xd7, 0xb3, 0x7b, 0x15, 0xf5, 0xda, 0xbc, 0x98, 0xab, 0x9, 0x7f, 0xb7, 0x31, 0xda, 0x1d, 0x25, 0xb1, 0xed, 0x9a, 0x8f, 0xee, 0x38, 0x42, 0x73, 0x3e, 0xc9, 0x2a, 0xd9, 0xd7, 0x2a, 0x23, 0xcd, 0x1d, 0x46, 0xe3, 0x67, 0xdf, 0xb7, 0xe1, 0x98, 0x41, 0xb8, 0x65, 0x16, 0x23, 0x79, 0x30, 0x58, 0x94, 0xcb, 0xcd, 0x44, 0x7f, 0x2e, 0x59, 0x86, 0xc, 0x69, 0xfd, 0x29, 0xf3, 0x5f, 0x4d, 0x66, 0x99, 0x21, 0x50, 0x59, 0x3f, 0xe8, 0xec, 0xfe, 0x83, 0xf3, 0x9f, 0xcf, 0x1, 0xc1, 0x58, 0x14, 0xba, 0xa4, 0x7f, 0x21, 0x0, 0x72, 0xee, 0x1f, 0x78, 0xba, 0x5e, 0x4f, 0xfd, 0x2b, 0x9d, 0x9, 0x4, 0x21, 0x74, 0x75, 0xc4, 0xee, 0x96, 0xf5, 0xb5, 0xf1, 0x5b, 0x17, 0xf7, 0x75, 0x19, 0x8b, 0x34, 0x1a, 0xf2, 0x4f, 0x83, 0xfa, 0x57, 0x42, 0x5d, 0x85, 0xaa, 0x55, 0x3b, 0xb8, 0xc2, 0x90, 0x9b, 0x1, 0x68, 0x8f, 0xb, 0xc9, 0x69, 0x97, 0x81, 0xf2, 0x8, 0x67, 0x5, 0x7d, 0xe5, 0xc1, 0x87, 0x7b, 0x9, 0x81, 0xdd, 0x29, 0xbd, 0x47, 0x43, 0x2d, 0x44, 0xeb, 0x1f, 0x7a, 0x3a, 0xf, 0x2, 0xf3, 0xc4, 0xdc, 0x1e, 0x98, 0x1d, 0x6a, 0xc, 0xc1, 0x12, 0x7b, 0xe6, 0xac, 0x24, 0x42, 0x6a, 0xa2, 0x12, 0x58, 0x22, 0xda, 0x46, 0x96, 0x15, 0x89, 0x5a, 0xe0, 0x5c, 0x19, 0x75, 0x31, 0xa9, 0x50, 0x2f, 0x90, 0xdc, 0xd5, 0x34, 0xf7, 0x47, 0x50, 0x43, 0x75, 0xa9, 0xdd, 0x65, 0x9, 0x24, 0xd6, 0x12, 0xe9, 0x25, 0xa7, 0x8e, 0x5, 0x25, 0xb, 0xbb, 0x3c, 0x2, 0x4d, 0x22, 0x58, 0x6, 0x42, 0xc1, 0x40, 0x12, 0x38, 0xaf, 0xcb, 0xa8, 0x14, 0x72, 0xe8, 0xc8, 0xf9, 0xa1, 0x8b, 0xbc, 0x83, 0x2c, 0xa6, 0x1d, 0x4d, 0xfd, 0x56, 0x14, 0xf9, 0x5c, 0x80, 0xbc, 0x78, 0xeb, 0x6b, 0x65, 0x0, 0xe5, 0x10, 0xb0, 0x24, 0x72, 0x0, 0x3c, 0x89, 0xbc, 0x8c, 0x38, 0x0, 0x2f, 0x23, 0xd6, 0x48, 0x1c, 0x0, 0x6f, 0x24, 0xde, 0xca, 0x38, 0x0, 0x6f, 0x65, 0x84, 0x4c, 0x1c, 0x0, 0x27, 0x13, 0x67, 0x28, 0xe, 0xc0, 0x8d, 0xb1, 0x81, 0xc2, 0x1, 0xf0, 0x81, 0xc2, 0x47, 0x1a, 0xa, 0xc0, 0x47, 0x1a, 0x3a, 0x54, 0x39, 0x99, 0x90, 0xa1, 0x8a, 0x8e, 0xf5, 0x74, 0x62, 0xac, 0xe3, 0xf, 0xcb, 0xed, 0x86, 0x3c, 0x2c, 0xd8, 0xd3, 0x96, 0x24, 0xf8, 0xd3, 0xa6, 0xfe, 0xb8, 0xce, 0x7f, 0xde, 0x6d, 0xfe, 0xbc, 0xab, 0x2d, 0x18, 0xca, 0x2b, 0xe, 0xbe, 0x64, 0x19, 0xd8, 0x92, 0x85, 0xaf, 0x79, 0x17, 0xbb, 0x97, 0xe1, 0xb, 0xb2, 0xe6, 0x61, 0x8b, 0x26, 0x11, 0x17, 0x4d, 0xf5, 0x55, 0x57, 0x7d, 0xd9, 0x56, 0x5f, 0xf7, 0xd5, 0x7f, 0x38, 0xd4, 0x7f, 0x79, 0xf0, 0x9f, 0xae, 0xba, 0x86, 0x9f, 0xae, 0xaf, 0x7c, 0x54, 0x7e, 0x1, 0x7, 0x4e, 0x88, 0x96, 0x1d, 0xbb, 0xbc, 0x56, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon1Name[] = { 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x64, 0x61, 0x72, 0x6b, 0x32, 0x78, 0x00 }; +const unsigned char defaultDialogIcon1Length[] = { 0x31, 0x32, 0x39, 0x35, 0x00 }; +const unsigned char defaultDialogIcon1Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf4, 0xe0, 0x91, 0xf9, 0x0, 0x0, 0x0, 0x96, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x25, 0xc5, 0xa8, 0x0, 0x0, 0x0, 0x31, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xfb, 0x55, 0x3, 0x61, 0x95, 0xc, 0xd7, 0xb7, 0x8a, 0x82, 0x39, 0xe5, 0x40, 0x19, 0x44, 0x2b, 0x4e, 0xee, 0xc6, 0xbf, 0x48, 0x25, 0x21, 0xe2, 0xb3, 0x67, 0xe8, 0x33, 0xd0, 0x6c, 0x42, 0x9, 0xcb, 0xf, 0xf6, 0xc2, 0xbc, 0xb0, 0xa8, 0x71, 0x52, 0x14, 0x7, 0x90, 0x7c, 0x8f, 0x11, 0x7b, 0xbe, 0x95, 0xf5, 0x71, 0x0, 0x0, 0x3, 0xf7, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x97, 0xd9, 0x72, 0xaa, 0x40, 0x10, 0x86, 0xdb, 0xc, 0xc8, 0xe, 0xa2, 0x2c, 0xee, 0x68, 0xdc, 0xf5, 0x98, 0xd4, 0xbc, 0xff, 0xcb, 0x9d, 0x4a, 0x9d, 0x53, 0xa1, 0x41, 0x98, 0x9e, 0x19, 0x30, 0x95, 0xb, 0xbf, 0xeb, 0x6, 0x7a, 0xf9, 0x7b, 0x1, 0x5e, 0xbc, 0x78, 0xf1, 0xe2, 0x85, 0x3e, 0x9b, 0x30, 0x5b, 0x16, 0x71, 0x5c, 0x2c, 0xb3, 0x70, 0x3, 0x3f, 0xcb, 0x30, 0x5a, 0x9b, 0x7, 0x9f, 0x23, 0xfc, 0x83, 0xb9, 0x8e, 0x86, 0xf0, 0x13, 0xdc, 0x2, 0x77, 0x32, 0xe0, 0x8d, 0xc, 0x26, 0x6e, 0x70, 0x83, 0xa7, 0xc2, 0xd2, 0xd3, 0x8e, 0xb, 0xd9, 0x9d, 0x52, 0x6, 0xcf, 0x62, 0xe4, 0x2c, 0xb8, 0x4, 0xb, 0x67, 0x4, 0xcf, 0xc0, 0xb3, 0xb9, 0x34, 0xb6, 0x7, 0x7d, 0x53, 0x24, 0x5c, 0x89, 0xa4, 0xe8, 0x37, 0xf9, 0x9, 0x57, 0x26, 0xe9, 0xaf, 0x10, 0x43, 0x67, 0xc0, 0x35, 0x18, 0x38, 0x3d, 0xf5, 0x65, 0x3c, 0xe5, 0x9a, 0x4c, 0x63, 0xe8, 0xe, 0x73, 0x78, 0x7, 0x1c, 0xd6, 0x39, 0xfd, 0xb3, 0xd6, 0xc, 0xfb, 0xdb, 0xa3, 0x79, 0x37, 0x8c, 0xbb, 0x79, 0xdc, 0xfa, 0xad, 0x35, 0x9a, 0x75, 0x2c, 0x43, 0x6e, 0xf1, 0x26, 0x56, 0xae, 0x17, 0x32, 0x40, 0xb0, 0xd0, 0x73, 0x57, 0xbc, 0x9, 0x2b, 0x87, 0xe, 0x4, 0xe3, 0x86, 0xc8, 0xf7, 0xe7, 0xbc, 0xc5, 0xdb, 0xf3, 0xbe, 0x21, 0x13, 0xe3, 0x0, 0xb4, 0x99, 0x3f, 0xbe, 0xcf, 0x36, 0xae, 0x20, 0xe0, 0x6a, 0xd8, 0x8f, 0x1e, 0xcf, 0x75, 0xe5, 0x67, 0xf2, 0x3a, 0x13, 0xf, 0x48, 0xbc, 0x9, 0xaf, 0x63, 0x32, 0xad, 0xef, 0x3f, 0xc8, 0xcf, 0xba, 0x80, 0x14, 0x17, 0xeb, 0x41, 0x8a, 0x3a, 0x1e, 0xd4, 0xe3, 0x5f, 0xcc, 0x99, 0xb4, 0xef, 0xf3, 0x45, 0x3d, 0x7, 0x1a, 0xf5, 0xaf, 0x15, 0xd2, 0x1d, 0x2a, 0x75, 0xaf, 0x5b, 0x93, 0x8f, 0xb2, 0xe, 0x82, 0x41, 0x55, 0xca, 0x5, 0x28, 0x12, 0x8d, 0xab, 0x1, 0x4, 0x8a, 0xfd, 0x5f, 0x7d, 0xdc, 0xca, 0x40, 0x99, 0xcc, 0xaa, 0x86, 0x90, 0x2b, 0x65, 0xb0, 0xfa, 0xb0, 0xfd, 0xe, 0x1a, 0xbc, 0xdb, 0xd5, 0x20, 0x86, 0xda, 0xd, 0xf0, 0xc1, 0x40, 0xb, 0xf6, 0xa1, 0xdb, 0xa, 0x4e, 0xa5, 0x7a, 0x6, 0x68, 0x63, 0x54, 0x94, 0xe4, 0x80, 0x24, 0x31, 0xc7, 0x18, 0xd0, 0x1, 0x83, 0x63, 0x62, 0x49, 0x1, 0x4c, 0x2b, 0xf9, 0x87, 0x4e, 0xfc, 0xe1, 0x88, 0xe9, 0x46, 0xbd, 0x0, 0x36, 0x83, 0x4e, 0x30, 0x5b, 0xb9, 0x8, 0x23, 0x5c, 0x37, 0x4b, 0xac, 0xff, 0x4f, 0xc3, 0x34, 0x8d, 0x4f, 0x71, 0x2f, 0x58, 0x58, 0x4f, 0x23, 0xa0, 0x49, 0x70, 0xf3, 0x8a, 0xfb, 0x3f, 0xf5, 0xbf, 0x8c, 0xfc, 0x14, 0x44, 0x64, 0x78, 0xa4, 0x24, 0x40, 0x52, 0x60, 0x87, 0x23, 0x10, 0xb1, 0x1c, 0xfc, 0x37, 0x5b, 0x82, 0x88, 0x8, 0xa7, 0xb4, 0x50, 0x4a, 0x80, 0xb, 0x42, 0x56, 0xdf, 0x7, 0x12, 0x8, 0x71, 0x55, 0x52, 0xe0, 0xe1, 0xfd, 0x27, 0x9e, 0x5d, 0x61, 0x69, 0x19, 0x8a, 0xdb, 0xa, 0xef, 0x46, 0xf, 0xc4, 0xd8, 0xf2, 0x1b, 0x2c, 0x2d, 0x2d, 0x53, 0x10, 0xb2, 0xc6, 0x6d, 0x45, 0xb4, 0x0, 0xee, 0x0, 0x26, 0x6d, 0x4b, 0x88, 0x9b, 0x59, 0x1c, 0xd9, 0x4a, 0xcf, 0x80, 0xb, 0xd5, 0xe2, 0xdf, 0xf2, 0x1e, 0x33, 0xea, 0x46, 0x92, 0x9d, 0x5, 0xc, 0x55, 0x6b, 0x2, 0x14, 0x73, 0xf9, 0x6b, 0x3, 0xdd, 0x89, 0x3e, 0x23, 0xca, 0x8a, 0xd4, 0x42, 0x61, 0x4a, 0xdf, 0x5b, 0x1e, 0x97, 0x13, 0xcc, 0x9, 0x8b, 0x45, 0x82, 0x78, 0xe6, 0xfb, 0xb3, 0x18, 0x30, 0xb4, 0xb8, 0x4f, 0xd0, 0xca, 0x6d, 0x47, 0x2f, 0xc1, 0xee, 0x6b, 0x71, 0x77, 0x83, 0x36, 0x2, 0x34, 0x4, 0xaf, 0xd0, 0x2b, 0x57, 0x34, 0xe, 0x3, 0x99, 0x91, 0xb5, 0x87, 0x9e, 0xd9, 0xa3, 0x1, 0x2b, 0xa3, 0xd5, 0x33, 0xf4, 0xcc, 0x59, 0xa2, 0xbf, 0x86, 0x28, 0x4d, 0x39, 0xf4, 0x4c, 0x8e, 0xca, 0xdb, 0x36, 0xe2, 0x23, 0xf4, 0xff, 0x2d, 0x79, 0xf8, 0x7e, 0xdd, 0x3, 0x92, 0x27, 0x33, 0xfa, 0x7b, 0x8f, 0xe8, 0x91, 0xed, 0x82, 0xc, 0xd1, 0xbf, 0x7b, 0xa0, 0xf6, 0x3e, 0x5a, 0x60, 0x6b, 0x68, 0xc6, 0x24, 0xa6, 0x10, 0x71, 0xf, 0x10, 0x78, 0xf4, 0x9f, 0xe2, 0x1, 0xed, 0x57, 0xa5, 0xa4, 0x6e, 0x1, 0x43, 0x6f, 0xef, 0x3, 0x34, 0xe3, 0x97, 0x32, 0x61, 0x40, 0x13, 0xaa, 0xf9, 0xcb, 0x4a, 0x89, 0x4f, 0xa1, 0x91, 0xd, 0x5a, 0x18, 0x40, 0x40, 0xdc, 0x3, 0x44, 0x7c, 0x7c, 0x43, 0x44, 0x24, 0x97, 0xd3, 0xb7, 0xd2, 0xfe, 0xd, 0x24, 0xd8, 0x52, 0x19, 0xcb, 0x4a, 0x83, 0xe3, 0x33, 0x1c, 0x38, 0x96, 0xf6, 0x59, 0xb3, 0xa8, 0x91, 0x4c, 0x3b, 0x39, 0x40, 0x37, 0xd9, 0x92, 0x5a, 0x45, 0xf7, 0x67, 0x38, 0x70, 0xa7, 0xd6, 0x51, 0x4c, 0xec, 0x62, 0xc2, 0x1, 0x95, 0x8d, 0x1c, 0xff, 0x4e, 0x7, 0xfe, 0xb6, 0x6f, 0x6, 0x29, 0xc, 0x2, 0x41, 0x10, 0x3c, 0xc4, 0x63, 0xc4, 0x78, 0x8a, 0x10, 0x90, 0x24, 0x67, 0x21, 0xfe, 0xff, 0x75, 0x1, 0x73, 0x89, 0x18, 0x28, 0xb1, 0xc, 0xb3, 0x2b, 0x3b, 0x1f, 0xb0, 0x41, 0xdd, 0x9d, 0xe9, 0xae, 0x9, 0x7f, 0x5, 0xe1, 0x1f, 0x61, 0xf8, 0x6f, 0x18, 0x7e, 0x10, 0xc1, 0x51, 0xc, 0x2, 0xc4, 0x51, 0xcc, 0x97, 0x11, 0xb, 0x50, 0x97, 0x11, 0x5f, 0xc7, 0x2c, 0x40, 0x5d, 0xc7, 0xdc, 0x90, 0xb0, 0x0, 0xd5, 0x90, 0x70, 0x4b, 0xc6, 0x2, 0x54, 0x4b, 0xc6, 0x4d, 0x29, 0xb, 0x50, 0x4d, 0x29, 0xb7, 0xe5, 0x2c, 0x40, 0xb5, 0xe5, 0x3c, 0x98, 0xb0, 0x0, 0x35, 0x98, 0xf0, 0x68, 0xc6, 0x2, 0xd4, 0x68, 0xc6, 0xc3, 0x29, 0xb, 0x50, 0xc3, 0x29, 0x8f, 0xe7, 0x2c, 0xc0, 0x8d, 0xe7, 0x6c, 0x50, 0xb0, 0x0, 0x6f, 0x50, 0xb0, 0x45, 0xc3, 0x2, 0xbc, 0x45, 0xc3, 0x26, 0x15, 0xb, 0x60, 0x93, 0xca, 0xd8, 0x74, 0x2c, 0x80, 0x6d, 0x3a, 0x65, 0x54, 0xb2, 0x0, 0x36, 0x2a, 0xd3, 0xb6, 0x6a, 0xe3, 0xcd, 0xea, 0x4d, 0x76, 0xfd, 0xc3, 0xd9, 0xf5, 0x3e, 0xb0, 0xb8, 0x88, 0xc0, 0x42, 0x45, 0x36, 0x4d, 0x37, 0x35, 0x58, 0x8d, 0x88, 0x6c, 0x64, 0x68, 0xf5, 0xac, 0x86, 0xa1, 0x82, 0x5c, 0x6b, 0x1e, 0x5a, 0xe5, 0x10, 0xdb, 0xc5, 0x7, 0x97, 0xe1, 0xd1, 0x6d, 0x7c, 0x78, 0xbd, 0x5b, 0x7c, 0x3f, 0x40, 0x7c, 0xff, 0x67, 0x80, 0xe1, 0xb5, 0x0, 0x18, 0x42, 0x11, 0x8e, 0xdb, 0xd5, 0x41, 0x2c, 0x8d, 0x86, 0x58, 0x72, 0xc3, 0x78, 0x7e, 0x80, 0x4c, 0xfd, 0x7a, 0x90, 0xa9, 0x5f, 0x80, 0x4c, 0x39, 0xa2, 0x5c, 0x3b, 0xc2, 0x6c, 0xc3, 0x29, 0x4f, 0x9c, 0x2f, 0x1, 0xa0, 0x31, 0x1e, 0xe9, 0x44, 0xa8, 0x75, 0xac, 0xaa, 0x11, 0xa0, 0xd6, 0xdc, 0xb1, 0xde, 0x78, 0xb0, 0xd9, 0xa0, 0xdd, 0xf7, 0xa3, 0xc0, 0xed, 0x9, 0xe0, 0xfd, 0x9, 0x2c, 0x38, 0x7c, 0x56, 0x3c, 0xce, 0x6b, 0x9e, 0x7e, 0x5e, 0xae, 0x78, 0x1c, 0x65, 0xc9, 0x25, 0x81, 0x35, 0x9f, 0xef, 0x45, 0xa7, 0x6e, 0xf6, 0xc7, 0x8b, 0x45, 0x27, 0xb5, 0xea, 0x55, 0xb7, 0x6d, 0x3d, 0xad, 0x7a, 0x95, 0x2a, 0x55, 0xaa, 0x54, 0xa9, 0xcd, 0xf5, 0x6, 0xc2, 0x69, 0xab, 0xf3, 0x71, 0x7e, 0x8e, 0x8, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon2Name[] = { 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x00 }; +const unsigned char defaultDialogIcon2Length[] = { 0x37, 0x36, 0x34, 0x00 }; +const unsigned char defaultDialogIcon2Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x9d, 0xb7, 0x81, 0xec, 0x0, 0x0, 0x0, 0x75, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x79, 0x59, 0x7d, 0x58, 0x0, 0x0, 0x0, 0x26, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xfc, 0xa5, 0xc9, 0xd, 0xf4, 0x7c, 0xd5, 0x8e, 0x7f, 0x26, 0x13, 0x78, 0xed, 0x47, 0xe0, 0xa0, 0x43, 0x8, 0xc5, 0xe9, 0xdc, 0xb4, 0xb0, 0x90, 0x88, 0x6b, 0x51, 0x29, 0x1f, 0xbe, 0x19, 0x94, 0x93, 0x37, 0x36, 0x1a, 0x58, 0x7b, 0xc0, 0x9a, 0x4f, 0x0, 0x0, 0x2, 0x10, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x57, 0xd9, 0xb2, 0x82, 0x30, 0xc, 0x15, 0x91, 0x4d, 0x40, 0x40, 0x94, 0x4d, 0xb9, 0xee, 0xfd, 0xff, 0x4f, 0xbc, 0x93, 0x14, 0xd, 0xd3, 0x2, 0x41, 0x3b, 0xbe, 0x99, 0xd1, 0x7, 0xd2, 0xe4, 0x34, 0x3d, 0x4d, 0xd3, 0x74, 0xf1, 0x93, 0xaf, 0x4a, 0xbb, 0x69, 0xea, 0x73, 0x9a, 0x9e, 0xeb, 0x66, 0xd3, 0xbe, 0xef, 0x9d, 0xf9, 0xb6, 0x23, 0x5e, 0xe2, 0xd8, 0x7e, 0xf6, 0x8e, 0x77, 0x18, 0x79, 0xe8, 0x67, 0x75, 0x82, 0x1f, 0x5e, 0x14, 0xce, 0x74, 0x5f, 0x5, 0x3b, 0x74, 0x7e, 0x21, 0x3c, 0xbf, 0x76, 0xc1, 0x6a, 0x8e, 0xff, 0x61, 0x8f, 0xf6, 0xf8, 0x97, 0x42, 0xdf, 0xfb, 0x3, 0xef, 0xef, 0xb, 0x81, 0x53, 0xe2, 0xd2, 0x13, 0xbb, 0x2c, 0xed, 0x4, 0xc9, 0xe8, 0x94, 0x3e, 0x17, 0x7e, 0x81, 0x33, 0xc1, 0x2f, 0x77, 0x6f, 0xe1, 0x16, 0x74, 0xdb, 0xf0, 0xe6, 0xe6, 0xa8, 0x84, 0xb1, 0x62, 0x72, 0x19, 0x6b, 0x4f, 0x48, 0xcb, 0xd8, 0x5d, 0x2b, 0x23, 0x6e, 0x2c, 0xe4, 0x98, 0xb7, 0x9e, 0xf0, 0x8f, 0xa5, 0x8d, 0x55, 0x9d, 0xf4, 0xc1, 0x53, 0x65, 0xc9, 0xd1, 0x78, 0x14, 0x61, 0xd5, 0xcd, 0x9f, 0x1f, 0x87, 0xc7, 0x8f, 0x76, 0x17, 0xc3, 0xd8, 0x2a, 0xa, 0x49, 0xdd, 0x72, 0x3c, 0xc4, 0xa5, 0xa4, 0xb3, 0x18, 0xe3, 0x1f, 0xfd, 0xdd, 0x29, 0x92, 0x2, 0x89, 0xe0, 0xf, 0xee, 0xbf, 0x8c, 0x2f, 0x58, 0x4c, 0x4a, 0x20, 0xad, 0xe, 0x3, 0x4, 0xec, 0x81, 0x3d, 0x2d, 0xfe, 0xcd, 0x46, 0x5d, 0x5, 0x9a, 0xed, 0x57, 0x3a, 0x34, 0xe, 0xd8, 0xa, 0xea, 0x9f, 0x10, 0x7f, 0x8a, 0x2d, 0x30, 0x39, 0x10, 0x68, 0xb8, 0xc3, 0xd8, 0x14, 0xfe, 0x5d, 0x48, 0x1d, 0x85, 0x94, 0x23, 0xae, 0x61, 0xa7, 0x9e, 0xac, 0x8, 0x71, 0x2b, 0x45, 0x9b, 0xa, 0xc7, 0x11, 0xa9, 0xa2, 0xac, 0xd0, 0x34, 0x52, 0xb4, 0x1e, 0xcc, 0x15, 0xab, 0xf9, 0xf3, 0x0, 0xd2, 0x1f, 0x8a, 0xf2, 0x1e, 0x83, 0xad, 0xa7, 0xd4, 0xf, 0xdc, 0x1d, 0x7d, 0x7, 0xa3, 0x24, 0x89, 0x34, 0xa5, 0x8b, 0xfb, 0x9d, 0xe9, 0x39, 0x60, 0xd, 0xe4, 0xe8, 0x76, 0x3b, 0x90, 0xf1, 0x96, 0x9e, 0xb, 0x36, 0x44, 0x95, 0x2f, 0x66, 0x4a, 0x2e, 0x2c, 0x65, 0xc3, 0x5a, 0x87, 0x56, 0x30, 0x9d, 0x7, 0xb4, 0x6, 0xa7, 0xed, 0x9b, 0x9, 0x90, 0x1b, 0x29, 0x94, 0x3c, 0x50, 0xe4, 0x8a, 0xe6, 0x7d, 0xe8, 0x6, 0x21, 0x43, 0x9d, 0x43, 0x88, 0x55, 0x67, 0x31, 0xc4, 0x80, 0x9b, 0x9e, 0xa6, 0x6, 0xc3, 0x44, 0xe7, 0x6b, 0x9, 0xfa, 0xa5, 0xce, 0x6c, 0x2, 0xfa, 0xba, 0xa7, 0x39, 0x13, 0x2b, 0x1c, 0x0, 0x71, 0x7e, 0xe9, 0x67, 0x1c, 0x28, 0xca, 0xf9, 0x0, 0x25, 0xe8, 0x53, 0x63, 0x0, 0xe3, 0x25, 0x18, 0x93, 0xc8, 0x6d, 0x23, 0x1, 0xf0, 0xdb, 0x48, 0x89, 0xc4, 0x3, 0x50, 0x22, 0x31, 0xa9, 0x4c, 0x0, 0x7c, 0x2a, 0xd3, 0x61, 0xe2, 0x1, 0xe8, 0x30, 0x71, 0xc7, 0x99, 0x0, 0xf8, 0xe3, 0x4c, 0x5, 0x85, 0x7, 0xa0, 0x82, 0xc2, 0x95, 0x34, 0x2, 0xe0, 0x4b, 0x1a, 0x15, 0x55, 0x16, 0x80, 0x8a, 0x2a, 0x5b, 0xd6, 0xe9, 0x38, 0x33, 0x65, 0xdd, 0xe8, 0x62, 0xe1, 0xaf, 0xb6, 0x2c, 0x63, 0xae, 0x36, 0xe3, 0xcb, 0x55, 0xbf, 0xde, 0x83, 0xf, 0xaf, 0x77, 0xf3, 0x6, 0x83, 0x5a, 0x1c, 0x9b, 0x6d, 0x71, 0x3e, 0x6a, 0xb2, 0xee, 0x4c, 0x93, 0xc5, 0xb5, 0x79, 0x81, 0xd6, 0xe6, 0xf1, 0x8d, 0xe6, 0xf5, 0xd9, 0x68, 0x5e, 0x99, 0x46, 0xd3, 0xa0, 0xd5, 0x35, 0x6a, 0xb6, 0xcd, 0xdb, 0x7d, 0xf3, 0x7, 0x87, 0xf9, 0x93, 0x87, 0x7f, 0x74, 0x5d, 0xd2, 0xf4, 0x82, 0x8f, 0xae, 0x9f, 0x7c, 0x53, 0xfe, 0x1, 0x6f, 0xc7, 0x5c, 0x26, 0x6e, 0x29, 0x2b, 0x15, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon3Name[] = { 0x69, 0x6e, 0x66, 0x6f, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x32, 0x78, 0x00 }; +const unsigned char defaultDialogIcon3Length[] = { 0x31, 0x32, 0x39, 0x33, 0x00 }; +const unsigned char defaultDialogIcon3Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf4, 0xe0, 0x91, 0xf9, 0x0, 0x0, 0x0, 0x93, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7a, 0x79, 0x23, 0x75, 0x0, 0x0, 0x0, 0x30, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xfb, 0x55, 0x5, 0xe4, 0x40, 0xa, 0xb7, 0xd7, 0x62, 0x44, 0xe, 0xc0, 0x94, 0xb2, 0x2b, 0x68, 0x4e, 0xee, 0x39, 0xc6, 0x81, 0x48, 0x21, 0x89, 0x18, 0xe8, 0x33, 0xd0, 0xbd, 0xcb, 0x96, 0x8f, 0x13, 0x8b, 0xf6, 0xe1, 0xa8, 0x83, 0x7c, 0x71, 0x6c, 0x52, 0x26, 0x25, 0x1a, 0x3b, 0x5d, 0x23, 0x0, 0xd6, 0x57, 0x0, 0x0, 0x3, 0xf9, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x97, 0xd7, 0x76, 0xab, 0x40, 0xc, 0x45, 0x35, 0xa1, 0x99, 0x6a, 0x63, 0xc, 0xb8, 0x13, 0xdc, 0x4b, 0x7c, 0xa3, 0xff, 0xff, 0xba, 0xbb, 0x6e, 0x59, 0x78, 0x26, 0x60, 0x24, 0x8a, 0xb3, 0xf2, 0xe0, 0xfd, 0x3c, 0x1e, 0xb, 0x95, 0x73, 0x34, 0xf0, 0xe2, 0xc5, 0x8b, 0x17, 0x2f, 0xda, 0x63, 0xde, 0xae, 0xb, 0x23, 0xc, 0x8d, 0xc5, 0xf5, 0x66, 0xc2, 0xf7, 0xe2, 0x3a, 0x67, 0x7f, 0xa5, 0xa3, 0x84, 0xbe, 0xf2, 0xcf, 0x8e, 0xb, 0xdf, 0x81, 0x66, 0x1c, 0x26, 0x2, 0x2b, 0x11, 0x93, 0x83, 0xa1, 0xc1, 0x53, 0xd1, 0x92, 0xfd, 0x16, 0x6b, 0xd9, 0xee, 0x93, 0xe7, 0xc5, 0x30, 0xdc, 0xcc, 0x91, 0xc1, 0x7c, 0x33, 0x84, 0x67, 0xf0, 0xcb, 0x42, 0x36, 0x56, 0x4, 0x7d, 0xe3, 0x78, 0xd8, 0x8, 0xcf, 0xe9, 0x37, 0xf9, 0x1e, 0x36, 0xc6, 0xeb, 0xaf, 0x10, 0xe6, 0x54, 0x60, 0xb, 0xc4, 0xb4, 0x27, 0x79, 0x8, 0x77, 0xd8, 0x92, 0x5d, 0x8, 0x3d, 0xb0, 0xc1, 0xe, 0x4c, 0xbb, 0x8b, 0x5e, 0xf0, 0x30, 0xc3, 0xfa, 0x3a, 0xc8, 0x8e, 0xb6, 0x7d, 0xcc, 0x82, 0xb5, 0xfe, 0xb0, 0x46, 0x41, 0x47, 0x79, 0x4c, 0x7, 0x58, 0xc5, 0x32, 0x8e, 0x66, 0xa0, 0x30, 0x8b, 0xe2, 0x25, 0x56, 0x31, 0x48, 0xa1, 0x3, 0xc6, 0x18, 0x4b, 0x8, 0xef, 0x94, 0x3e, 0x88, 0xf6, 0xe4, 0x9, 0x2c, 0x31, 0x36, 0xa0, 0x35, 0xa3, 0xf2, 0x7d, 0x96, 0x9d, 0x43, 0xd, 0xb9, 0x6d, 0x95, 0x23, 0x1e, 0xb5, 0x15, 0x7e, 0x1f, 0xbf, 0x32, 0x89, 0x18, 0x8a, 0x39, 0xc1, 0xaf, 0xf8, 0x5a, 0xab, 0xff, 0xb7, 0x4a, 0xe5, 0xbc, 0x0, 0x8b, 0xcb, 0xa0, 0x94, 0xb7, 0x36, 0x11, 0xf8, 0xa8, 0x32, 0x1f, 0x69, 0xec, 0xd8, 0x47, 0x73, 0x54, 0xf1, 0x5b, 0xd4, 0x1f, 0x15, 0x44, 0x6c, 0x36, 0x12, 0xcf, 0x58, 0xa0, 0x42, 0xe3, 0x3e, 0x30, 0x84, 0xda, 0xca, 0x4e, 0x63, 0xf7, 0x52, 0x7, 0x48, 0x18, 0xd, 0xe7, 0x7f, 0xac, 0x56, 0xff, 0x3, 0x1a, 0xf3, 0x31, 0x50, 0x3f, 0x21, 0x6d, 0xa4, 0x7f, 0xea, 0x8f, 0x2d, 0xb7, 0x95, 0x88, 0x5a, 0xea, 0x47, 0x34, 0xb9, 0x44, 0xd5, 0xdf, 0x77, 0xad, 0xe5, 0x1c, 0xbf, 0xab, 0xaa, 0xc, 0x6c, 0xa6, 0x4a, 0xf5, 0x6c, 0x68, 0x8d, 0x2d, 0x5a, 0x39, 0x53, 0x88, 0x32, 0x36, 0x74, 0xc0, 0x46, 0x99, 0x90, 0x39, 0x42, 0x3b, 0x25, 0xff, 0xd0, 0x9, 0xa5, 0xa, 0x3b, 0x93, 0x59, 0x0, 0xbe, 0x86, 0xf1, 0xf5, 0x94, 0x5f, 0x84, 0xa1, 0xe0, 0xb7, 0x6e, 0xfe, 0xe9, 0xfb, 0x9f, 0x39, 0x7f, 0xa0, 0xc4, 0x10, 0x68, 0x3c, 0x79, 0x78, 0xeb, 0xe7, 0x3f, 0xd1, 0x11, 0x11, 0xf5, 0xa4, 0x5e, 0xf, 0x64, 0x49, 0xf1, 0x80, 0xc4, 0x91, 0x3, 0xae, 0xd7, 0xbf, 0x85, 0xf8, 0x7f, 0x6c, 0x51, 0x7f, 0xa3, 0x9c, 0x52, 0xa3, 0x51, 0x2, 0x62, 0xa8, 0x65, 0x59, 0x2c, 0x48, 0x50, 0xcb, 0x1, 0xef, 0xac, 0x48, 0x37, 0x97, 0xfd, 0xaf, 0xbe, 0x69, 0x6f, 0x58, 0x30, 0xab, 0x1f, 0x2b, 0xd9, 0x1b, 0xa9, 0x8d, 0xc2, 0xe2, 0x3b, 0x58, 0x82, 0x5, 0x9, 0xdf, 0x59, 0x2d, 0x62, 0x4, 0xe4, 0x9, 0xd0, 0xd8, 0x67, 0x89, 0xe6, 0xd6, 0x6, 0xc8, 0x3d, 0xbb, 0xc1, 0x3b, 0x17, 0x6a, 0xc4, 0xc7, 0xc5, 0xb0, 0x50, 0x62, 0x71, 0xc1, 0x3b, 0x9b, 0xda, 0x3b, 0xa5, 0x6a, 0x4d, 0x80, 0x62, 0xc4, 0xdf, 0x36, 0xa4, 0x3d, 0x51, 0xd7, 0x78, 0x65, 0xc5, 0x8, 0x48, 0x32, 0xfc, 0x4b, 0xc6, 0xd8, 0x54, 0x91, 0xd7, 0x30, 0x7b, 0xa5, 0x59, 0x68, 0xc2, 0x40, 0xd7, 0x3, 0xd5, 0x62, 0xe8, 0xe6, 0xde, 0xd7, 0x54, 0x60, 0x4b, 0x9b, 0x60, 0x77, 0x5b, 0xdc, 0x3e, 0xae, 0x81, 0x21, 0x89, 0x60, 0xe, 0xbd, 0x92, 0xb, 0x8e, 0x1a, 0x1e, 0x68, 0xd1, 0xa6, 0xa1, 0x15, 0x36, 0xe6, 0xf4, 0xea, 0x9, 0x7a, 0xe6, 0xc4, 0x98, 0x2f, 0x57, 0x4a, 0x53, 0xa, 0x3d, 0x93, 0x4a, 0xe5, 0x35, 0x69, 0x23, 0x5c, 0x2, 0xb, 0xf7, 0xcf, 0x3e, 0xe0, 0x2, 0xb, 0xe9, 0xf5, 0xfe, 0xc8, 0x64, 0xcf, 0x6a, 0x99, 0x68, 0x9c, 0x7f, 0xfb, 0x80, 0x3, 0x1c, 0x62, 0x2c, 0x38, 0x43, 0x35, 0x3e, 0xa1, 0x42, 0xc4, 0x3e, 0x40, 0x10, 0xd1, 0x2f, 0xc5, 0x95, 0xea, 0xaf, 0xfc, 0xa4, 0xae, 0x81, 0xc1, 0x8d, 0x5e, 0xa, 0xf4, 0x7b, 0x9b, 0x0, 0x83, 0x59, 0xb3, 0x78, 0x35, 0x71, 0xb7, 0x3, 0xa8, 0xc4, 0x44, 0xe2, 0x4, 0x77, 0x1f, 0x20, 0xbf, 0xf, 0x4d, 0x22, 0x47, 0xbc, 0x9c, 0xbe, 0x61, 0xc1, 0x1b, 0x30, 0x58, 0x53, 0x19, 0xbb, 0x12, 0xcf, 0x38, 0x32, 0x0, 0xfe, 0x83, 0xf3, 0x5a, 0xdd, 0xd4, 0x58, 0x90, 0x3d, 0x23, 0x80, 0xc, 0xb, 0x16, 0x94, 0x15, 0x1d, 0x9f, 0x11, 0xc0, 0x91, 0xb2, 0xa3, 0x90, 0xf0, 0x62, 0x32, 0x0, 0xbe, 0x23, 0x87, 0x3f, 0x33, 0x80, 0xdf, 0xed, 0x5b, 0xcb, 0xe, 0x82, 0x30, 0x10, 0x6c, 0x2, 0xed, 0x85, 0x44, 0xf, 0x84, 0x28, 0x26, 0x18, 0x4e, 0xdc, 0x14, 0xfe, 0xff, 0xeb, 0x8c, 0x21, 0x5e, 0x1c, 0xc9, 0xd2, 0x4e, 0x65, 0x29, 0xe9, 0x7c, 0xc1, 0x24, 0x4a, 0x77, 0xe7, 0xb1, 0xea, 0x3f, 0x81, 0xfa, 0x9f, 0x50, 0xfd, 0x33, 0x54, 0x7f, 0x88, 0xf0, 0x29, 0xe6, 0x9, 0xc8, 0x4f, 0xb1, 0x3c, 0x8c, 0x78, 0x2, 0x38, 0x8c, 0xd8, 0x71, 0x8c, 0x4, 0xd8, 0x71, 0x8c, 0xb, 0x9, 0x43, 0x40, 0x5e, 0x48, 0xd8, 0x95, 0xc, 0x9, 0xb0, 0x2b, 0x19, 0x2e, 0xa5, 0xc, 0x1, 0x79, 0x29, 0x65, 0xd7, 0x72, 0x24, 0x40, 0xaf, 0xe5, 0x28, 0x4c, 0x8, 0x2, 0xa2, 0x30, 0xa1, 0xa5, 0x19, 0x12, 0xe0, 0xa5, 0x19, 0x8a, 0x53, 0x82, 0x80, 0x24, 0x4e, 0x69, 0x79, 0x8e, 0x4, 0x78, 0x79, 0x8e, 0x6, 0x5, 0x41, 0x40, 0x32, 0x28, 0x78, 0x8b, 0x6, 0x9, 0xf0, 0x16, 0xd, 0x9a, 0x54, 0xc, 0x1, 0xd9, 0xa4, 0xe2, 0x6d, 0x3a, 0x24, 0xc0, 0xdb, 0x74, 0x68, 0x54, 0xf2, 0x4, 0xd0, 0xa8, 0xdc, 0xb7, 0x55, 0x6b, 0xea, 0xf5, 0x66, 0x75, 0x4b, 0x98, 0xd5, 0x91, 0xed, 0xfa, 0x3b, 0x67, 0xd7, 0xf3, 0x81, 0xc5, 0x99, 0x8, 0x2c, 0x88, 0xc8, 0x6, 0x2c, 0x1a, 0xef, 0xc8, 0x86, 0xf, 0xad, 0xaa, 0xb1, 0xef, 0xc7, 0xca, 0x27, 0xb4, 0x4a, 0x21, 0xb6, 0xd3, 0xf, 0x2e, 0xd5, 0xa3, 0x5b, 0xfd, 0xf0, 0x5a, 0x3f, 0xbe, 0xd7, 0x2f, 0x30, 0xfc, 0xa1, 0xc2, 0x71, 0xbb, 0xa4, 0x55, 0x62, 0xc1, 0x1a, 0xcf, 0xe0, 0x57, 0xe3, 0x19, 0xbe, 0x6b, 0x3c, 0xe9, 0x15, 0x99, 0xa2, 0x56, 0xb9, 0x4e, 0x45, 0xac, 0x32, 0xdb, 0x33, 0xb0, 0xcc, 0x96, 0x66, 0x9d, 0x6f, 0x7, 0x85, 0x46, 0x8f, 0x4a, 0xe7, 0x63, 0xb9, 0xd2, 0x99, 0x76, 0xa9, 0x75, 0x9e, 0x4c, 0xe1, 0xb8, 0x1e, 0xa1, 0xd8, 0xac, 0x5f, 0xed, 0xd6, 0x2f, 0xb7, 0xbf, 0x61, 0xbb, 0xc9, 0xb, 0x9d, 0x35, 0xb1, 0x51, 0xf3, 0x7, 0xe, 0xfc, 0x89, 0x47, 0x39, 0xad, 0x40, 0x9, 0x27, 0x1e, 0x87, 0x39, 0x72, 0x99, 0x39, 0xd8, 0x66, 0xf9, 0xcc, 0xa7, 0xb1, 0x85, 0xd9, 0x2, 0xee, 0xf7, 0xa1, 0x93, 0x33, 0xdb, 0xc2, 0xb5, 0x9f, 0x53, 0xaf, 0xd6, 0x99, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x60, 0xbc, 0x0, 0xd9, 0xfc, 0x9e, 0xf5, 0xee, 0xae, 0x37, 0x12, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon4Name[] = { 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x64, 0x61, 0x72, 0x6b, 0x00 }; +const unsigned char defaultDialogIcon4Length[] = { 0x39, 0x34, 0x31, 0x00 }; +const unsigned char defaultDialogIcon4Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x9d, 0xb7, 0x81, 0xec, 0x0, 0x0, 0x0, 0x99, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x1d, 0x4, 0x54, 0x0, 0x0, 0x0, 0x32, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xf9, 0x1c, 0xe6, 0xb1, 0x3b, 0x17, 0xc9, 0xad, 0x6, 0x8f, 0x4e, 0x4b, 0x40, 0xf6, 0xe3, 0xe0, 0xc1, 0x20, 0x11, 0x62, 0x54, 0x34, 0x30, 0x2b, 0x5d, 0xd6, 0xd0, 0x98, 0x76, 0x65, 0xb, 0x9b, 0x92, 0x6d, 0x48, 0x2, 0x9, 0x3, 0x9c, 0xb5, 0x9d, 0xed, 0xe8, 0xbb, 0x78, 0xdc, 0x83, 0x71, 0x9f, 0x76, 0x53, 0x1c, 0x31, 0x0, 0x0, 0x2, 0x91, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x57, 0x6b, 0x7b, 0xaa, 0x30, 0xc, 0x3e, 0xdc, 0x4, 0x41, 0x85, 0x71, 0x13, 0x54, 0x18, 0x53, 0xa7, 0xce, 0xcb, 0xb6, 0xb3, 0xff, 0xff, 0xe3, 0x8e, 0xa4, 0xa5, 0x29, 0x4, 0xe, 0xee, 0xe9, 0xb3, 0x6f, 0xcb, 0x27, 0xdb, 0x92, 0x34, 0x79, 0xf3, 0x26, 0x8d, 0x7f, 0x7e, 0xe5, 0x47, 0x65, 0xaf, 0xa7, 0x5e, 0x5c, 0x14, 0xb1, 0x97, 0xea, 0xfb, 0xef, 0x6b, 0x4f, 0x3c, 0xd7, 0xf6, 0xb5, 0x2f, 0x10, 0xcd, 0xb7, 0x5d, 0x6f, 0xf2, 0x2d, 0x75, 0x6b, 0x13, 0x7c, 0x75, 0x24, 0xd8, 0x58, 0xf, 0xab, 0xcf, 0xcd, 0xfa, 0x6a, 0x22, 0x9a, 0x39, 0x7f, 0x48, 0x3d, 0x3c, 0xa, 0x75, 0x62, 0xe2, 0x18, 0x8e, 0xeb, 0xcf, 0x2, 0x59, 0xc3, 0x58, 0xad, 0xc, 0xd9, 0x5e, 0x30, 0x1b, 0xd3, 0xdf, 0x2e, 0x9a, 0x6f, 0xd, 0xa7, 0xb4, 0x32, 0x3d, 0x8a, 0xf4, 0xcc, 0x2a, 0x1d, 0xa3, 0xd9, 0x5d, 0x6c, 0xff, 0xab, 0xfe, 0xfc, 0x22, 0xe2, 0x4d, 0x74, 0xf9, 0x40, 0x4f, 0x4, 0x2e, 0x2f, 0xcf, 0xc3, 0xfa, 0xaf, 0xe, 0xff, 0xe8, 0x69, 0xd7, 0x13, 0xdb, 0x13, 0x3f, 0x74, 0x5e, 0x7, 0xd, 0xf0, 0xfb, 0x8d, 0x7c, 0xda, 0x77, 0x3a, 0xcd, 0xd, 0xee, 0xc3, 0x60, 0xfc, 0xec, 0x7c, 0xbd, 0x1c, 0xfa, 0x60, 0xb9, 0x66, 0x5f, 0x6c, 0x7, 0xf0, 0x67, 0xf8, 0x99, 0x13, 0x54, 0x28, 0x1c, 0xdb, 0x36, 0x6f, 0x68, 0x70, 0x62, 0x32, 0x24, 0x7b, 0x73, 0x11, 0xfa, 0x4c, 0x5f, 0x60, 0xa4, 0x57, 0xb, 0x8e, 0xfc, 0xdf, 0x50, 0xa0, 0xcc, 0x2c, 0xf8, 0x7d, 0x7c, 0x78, 0x63, 0xfe, 0x8b, 0xfb, 0xd3, 0x15, 0xa6, 0xff, 0xdd, 0x12, 0x56, 0x59, 0x14, 0x6f, 0x3d, 0xfc, 0xd5, 0x0, 0x3f, 0xe1, 0x6e, 0xe6, 0xa3, 0xbe, 0xbc, 0xbf, 0x4, 0x24, 0x35, 0xc2, 0xea, 0x3, 0xf3, 0x2d, 0x17, 0x90, 0x43, 0xd2, 0x50, 0x6c, 0x91, 0xbb, 0x9c, 0x45, 0x7a, 0xe8, 0xd6, 0x9f, 0x6, 0xf9, 0x17, 0xf9, 0x4b, 0x38, 0x21, 0x5c, 0x97, 0x5b, 0x8a, 0x5, 0x59, 0x60, 0x43, 0xeb, 0xd6, 0xe6, 0x6, 0x76, 0x91, 0x3f, 0x27, 0x58, 0x7f, 0xde, 0xd, 0x4e, 0x3f, 0x99, 0x6d, 0xd1, 0x54, 0x76, 0xb0, 0xde, 0x74, 0xfa, 0x47, 0x0, 0x7e, 0x21, 0x75, 0xcf, 0xf5, 0xba, 0x62, 0x8b, 0xaa, 0xfe, 0x7d, 0x16, 0xc0, 0xef, 0x21, 0xda, 0xa0, 0xdd, 0x61, 0x3c, 0x70, 0x33, 0x11, 0xeb, 0x14, 0x32, 0x38, 0x6f, 0xf0, 0x45, 0x9f, 0x31, 0x3c, 0xaf, 0x65, 0xc0, 0x5, 0xa8, 0xf5, 0x76, 0x4e, 0xce, 0xfc, 0x92, 0x10, 0xac, 0x61, 0x78, 0x3a, 0x24, 0xc2, 0x6d, 0xd1, 0xdc, 0x86, 0x2a, 0x91, 0x42, 0x7a, 0xbf, 0xaf, 0x4f, 0x1c, 0xd2, 0x59, 0x17, 0x35, 0x7, 0xf2, 0x22, 0x17, 0x8c, 0xe, 0x49, 0x2f, 0x65, 0x5a, 0xd8, 0xe7, 0x53, 0xc8, 0x33, 0xc, 0x80, 0x5e, 0xa4, 0x98, 0x4b, 0x60, 0xa3, 0x5c, 0xef, 0x29, 0x0, 0x6b, 0xb5, 0x9c, 0x8a, 0xf6, 0xd, 0xa3, 0x59, 0x11, 0xcb, 0x39, 0x7, 0x97, 0xd2, 0x2e, 0x86, 0x5a, 0x46, 0xf9, 0x99, 0x7d, 0x9c, 0x2e, 0xac, 0x1e, 0x52, 0x79, 0x17, 0xee, 0x93, 0x2b, 0x2a, 0x66, 0x18, 0x52, 0x7d, 0x80, 0x8b, 0x90, 0x9f, 0xa1, 0x18, 0x4b, 0x3b, 0x45, 0xbd, 0xb1, 0x8a, 0x88, 0x81, 0x8f, 0x46, 0xff, 0xda, 0xca, 0x7a, 0x4, 0x75, 0x56, 0x3c, 0x60, 0xe0, 0xd8, 0x74, 0x31, 0x38, 0x22, 0x6, 0xc6, 0x43, 0xa8, 0x0, 0x9b, 0x75, 0x89, 0x19, 0x23, 0x21, 0x10, 0x10, 0xa9, 0x1, 0x7, 0xdb, 0x30, 0x1, 0x91, 0xa4, 0x91, 0x18, 0x20, 0x65, 0x43, 0xd2, 0x48, 0x88, 0x44, 0x30, 0x38, 0xe2, 0x9a, 0x12, 0x89, 0x52, 0x99, 0x94, 0x8, 0x92, 0x9e, 0x52, 0x99, 0x16, 0x53, 0x47, 0x76, 0x72, 0x15, 0x91, 0x62, 0x1a, 0x2e, 0x67, 0x94, 0xd8, 0xb6, 0x11, 0x6c, 0x5a, 0xce, 0xb4, 0xa1, 0x10, 0x39, 0xc8, 0xbd, 0x8f, 0x36, 0x14, 0xda, 0xd2, 0xc6, 0x85, 0xb6, 0x34, 0xda, 0x54, 0x51, 0xa2, 0x3c, 0x47, 0x12, 0x92, 0xa6, 0x3a, 0xd8, 0xd6, 0x11, 0x2e, 0xfb, 0x8e, 0x36, 0x42, 0x4b, 0xda, 0xfa, 0xc0, 0xc3, 0x82, 0x72, 0xab, 0xf7, 0x6e, 0xed, 0x7, 0xf6, 0x42, 0x1f, 0x16, 0xfa, 0xb4, 0x51, 0x1e, 0xd0, 0xa7, 0x6d, 0xfc, 0x71, 0x5, 0xb7, 0xf0, 0x32, 0xfa, 0xb8, 0xe, 0x3f, 0xef, 0x28, 0xc9, 0xf5, 0x9a, 0xc8, 0xf7, 0xe3, 0xf3, 0xae, 0x36, 0x60, 0x28, 0x8f, 0x38, 0xe3, 0x43, 0xd6, 0x6c, 0x64, 0xc8, 0x52, 0x1b, 0xf3, 0x14, 0x7, 0x4d, 0xd5, 0x51, 0x57, 0x6d, 0xd8, 0xae, 0x42, 0xc5, 0x71, 0x5f, 0xf1, 0xf, 0x87, 0xe2, 0x5f, 0x1e, 0xc5, 0x3f, 0x5d, 0xbf, 0xf2, 0x93, 0xf2, 0xf, 0x45, 0x13, 0xb6, 0x9d, 0x6, 0x38, 0x84, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon5Name[] = { 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x64, 0x61, 0x72, 0x6b, 0x32, 0x78, 0x00 }; +const unsigned char defaultDialogIcon5Length[] = { 0x31, 0x35, 0x35, 0x37, 0x00 }; +const unsigned char defaultDialogIcon5Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf4, 0xe0, 0x91, 0xf9, 0x0, 0x0, 0x0, 0x9f, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa, 0x77, 0x79, 0xb8, 0x0, 0x0, 0x0, 0x34, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xfb, 0x2, 0xa, 0x40, 0x62, 0xe5, 0x8a, 0x82, 0xe, 0x6, 0xb8, 0x2b, 0x19, 0x44, 0xed, 0xd9, 0x26, 0xb1, 0x94, 0x4e, 0x39, 0xc6, 0xf6, 0xbf, 0x55, 0x48, 0x21, 0x67, 0x33, 0xe0, 0xa8, 0x6c, 0xe8, 0xc2, 0x97, 0x90, 0x14, 0xd4, 0xb5, 0x3b, 0xd1, 0xcb, 0x7b, 0x52, 0xbc, 0x9e, 0xef, 0x71, 0x72, 0x5c, 0x1d, 0xd8, 0xaf, 0x95, 0x64, 0x0, 0x0, 0x4, 0xf1, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x97, 0xdb, 0x96, 0xa2, 0x40, 0xc, 0x45, 0x23, 0x20, 0x20, 0x88, 0x82, 0x22, 0xd8, 0xde, 0xef, 0xed, 0xa5, 0x45, 0xa7, 0x27, 0xff, 0xff, 0x6d, 0xf3, 0x30, 0x6b, 0x69, 0xa, 0x52, 0x1d, 0x40, 0xed, 0x27, 0xf7, 0x6b, 0x61, 0xaa, 0xea, 0x24, 0x27, 0x29, 0xe1, 0xcd, 0x9b, 0x37, 0x6f, 0xde, 0xd4, 0xc7, 0xc, 0xa2, 0xa9, 0x15, 0xc7, 0xd6, 0x34, 0xa, 0x4c, 0xf8, 0x5d, 0x5a, 0x9d, 0x45, 0xff, 0x6c, 0x23, 0xc1, 0x3e, 0xf7, 0x17, 0x9d, 0x16, 0xfc, 0x6, 0x9e, 0xe5, 0x26, 0xd, 0x64, 0x69, 0x24, 0xae, 0xe5, 0xc1, 0x4b, 0x31, 0x46, 0xf3, 0x14, 0x7f, 0x24, 0x9d, 0x8f, 0xc, 0x78, 0x15, 0x6d, 0x67, 0x89, 0x25, 0x58, 0x3a, 0x6d, 0x78, 0x5, 0x7b, 0x1f, 0x4b, 0xe3, 0xf, 0xe0, 0xd9, 0x74, 0x86, 0x58, 0x89, 0x61, 0xe7, 0xb9, 0xe2, 0xf, 0xb1, 0x32, 0xc3, 0xe7, 0x25, 0xc2, 0x74, 0x1a, 0x58, 0x83, 0x86, 0x63, 0xc2, 0x53, 0x88, 0x67, 0x58, 0x93, 0x59, 0xfc, 0xc, 0xe7, 0x39, 0xf8, 0x0, 0xce, 0xc3, 0x9e, 0x6c, 0x7d, 0x6a, 0x15, 0xb6, 0x8f, 0xbd, 0xf5, 0xa9, 0xd9, 0x3c, 0xad, 0x7b, 0x47, 0x5b, 0x9b, 0xa3, 0xde, 0x83, 0xed, 0x71, 0xbc, 0x43, 0x8e, 0x83, 0x3b, 0x8, 0xc, 0x45, 0xa7, 0x60, 0xe0, 0x1e, 0x90, 0x63, 0x37, 0x86, 0x7, 0xb0, 0xba, 0xcc, 0xcd, 0x57, 0x57, 0x4d, 0xcc, 0xf1, 0x75, 0xc5, 0x28, 0xd1, 0xb5, 0xa0, 0x36, 0x93, 0x62, 0x3c, 0xbf, 0xb9, 0x85, 0x1f, 0xd8, 0x36, 0xfd, 0xe2, 0x89, 0x27, 0x75, 0xc7, 0x4e, 0x1f, 0xf3, 0x24, 0x3, 0x10, 0xd9, 0x27, 0x98, 0xa7, 0xef, 0xd5, 0xda, 0xbf, 0x50, 0x7e, 0xe1, 0x17, 0x94, 0xe2, 0x2b, 0xc4, 0x1c, 0x9f, 0x75, 0x4e, 0x90, 0xbf, 0xff, 0x72, 0x52, 0x3a, 0x8a, 0x37, 0x59, 0xe6, 0x35, 0xa8, 0x91, 0xff, 0x5c, 0x22, 0x5d, 0x13, 0x2a, 0x60, 0xba, 0xb9, 0xf2, 0xa9, 0x5c, 0x7, 0x96, 0x1a, 0xa0, 0xdb, 0xa9, 0x3c, 0xbd, 0xba, 0xea, 0x5, 0xac, 0x8a, 0xfe, 0x57, 0x7f, 0x1e, 0x46, 0x50, 0x99, 0x28, 0x54, 0xaf, 0x30, 0xae, 0xd4, 0xff, 0x76, 0xaa, 0xf7, 0x5a, 0xb5, 0x9a, 0xa8, 0xea, 0xc8, 0x5d, 0x85, 0x20, 0x86, 0x6a, 0x80, 0x8d, 0xa1, 0xf1, 0x7c, 0x36, 0xc, 0xd3, 0x74, 0xb6, 0x9a, 0x2f, 0x2, 0x3e, 0xcc, 0x6, 0x29, 0x3d, 0x3, 0xca, 0xe2, 0x28, 0xd9, 0x6b, 0xb2, 0xc1, 0x9b, 0x4a, 0xd7, 0x3b, 0x2e, 0x58, 0x8b, 0x34, 0x95, 0x4a, 0x72, 0xa0, 0x24, 0x31, 0x52, 0xd8, 0xfd, 0x47, 0xbb, 0xc2, 0xe8, 0xfd, 0x60, 0x4f, 0x80, 0x94, 0xb8, 0xa4, 0x85, 0x66, 0x8a, 0xfe, 0x5c, 0x76, 0x7b, 0xc8, 0xb0, 0xe2, 0x12, 0xb1, 0x51, 0xe, 0x69, 0x56, 0x4f, 0x80, 0x6f, 0x8, 0x23, 0x92, 0x30, 0x9b, 0x32, 0xa9, 0xf2, 0x2b, 0x27, 0xa1, 0x4d, 0xf3, 0x16, 0x32, 0xa5, 0x3b, 0xed, 0xa2, 0x86, 0xb4, 0xc3, 0xa8, 0x15, 0xd2, 0x7a, 0x6a, 0x83, 0xcc, 0x90, 0x9a, 0x97, 0xf1, 0x7f, 0x60, 0xa3, 0x96, 0xb, 0xf3, 0x7d, 0x44, 0xcf, 0x3b, 0x4, 0x91, 0xe, 0x3d, 0x30, 0x73, 0x23, 0xf3, 0x88, 0x3f, 0x10, 0x6e, 0x99, 0x88, 0x54, 0x52, 0xab, 0x92, 0x0, 0xae, 0x30, 0xa3, 0x18, 0xe6, 0x50, 0xc4, 0xad, 0x22, 0xc1, 0x9e, 0xce, 0x3f, 0x93, 0x11, 0x54, 0x7a, 0xa1, 0xf, 0x18, 0xd1, 0x96, 0xec, 0x3a, 0x8f, 0x2f, 0x4c, 0xb0, 0x9c, 0x1, 0xd3, 0x24, 0x49, 0x73, 0x66, 0x84, 0x22, 0xb, 0xfa, 0x34, 0x10, 0x2c, 0x40, 0xf3, 0xe9, 0x31, 0xe, 0x40, 0xca, 0xa6, 0x6d, 0x0, 0x18, 0xed, 0xd, 0x52, 0x98, 0xba, 0xf1, 0xa8, 0x13, 0xda, 0xa5, 0x7b, 0xc0, 0x97, 0xb0, 0x3e, 0xbb, 0x15, 0x94, 0x45, 0x5b, 0xd7, 0x9a, 0x7b, 0x23, 0x95, 0xed, 0x5, 0x6, 0xc9, 0x56, 0x2, 0xc, 0x21, 0xb1, 0x68, 0x40, 0xac, 0x49, 0xac, 0xb6, 0xe4, 0x66, 0xe, 0x79, 0x27, 0xda, 0x6, 0xe8, 0x19, 0x9, 0xd5, 0x12, 0x91, 0xf5, 0x6f, 0x20, 0x7c, 0xd3, 0x1f, 0xa, 0xb5, 0x3d, 0x2, 0x3d, 0x73, 0xd2, 0x83, 0x81, 0xe1, 0x43, 0xbb, 0xee, 0xb, 0xaf, 0x2f, 0x9f, 0x3a, 0x55, 0x8b, 0x97, 0xa, 0x43, 0xf0, 0xf, 0xde, 0xb8, 0x6a, 0x57, 0x5c, 0x61, 0x2c, 0xa6, 0x1e, 0xe8, 0xb0, 0x48, 0x13, 0xdc, 0xa, 0x35, 0xba, 0xd7, 0x6a, 0x9c, 0x1, 0xc3, 0xb6, 0x51, 0xa6, 0x1b, 0xba, 0xd4, 0xce, 0x1c, 0xd9, 0xfd, 0x80, 0x26, 0x28, 0xb4, 0x24, 0xa7, 0xaf, 0x58, 0x89, 0xf4, 0xb5, 0x7a, 0x15, 0x4e, 0xb8, 0xd3, 0x37, 0x90, 0x39, 0x8, 0xd9, 0x4b, 0x40, 0x43, 0x8b, 0xc8, 0x34, 0x16, 0x5c, 0x92, 0xe9, 0x73, 0xbc, 0x1, 0x8e, 0x31, 0x12, 0xf5, 0xe4, 0x41, 0x78, 0x10, 0x3a, 0xf5, 0xe5, 0x2f, 0xa8, 0xac, 0xf1, 0xc6, 0x9, 0x58, 0xe, 0xb4, 0x59, 0xf2, 0x2c, 0xe4, 0x34, 0x5, 0x87, 0xff, 0xfb, 0xc7, 0xf9, 0xc, 0xa4, 0x24, 0xbc, 0x58, 0x60, 0xb, 0xf9, 0xdf, 0xe0, 0x40, 0xeb, 0xd4, 0xd3, 0xea, 0xb2, 0xcb, 0xf2, 0xf7, 0x37, 0x8e, 0xb2, 0xc0, 0x3, 0xf9, 0x9f, 0xe2, 0x19, 0x6f, 0x4, 0x50, 0x5, 0x33, 0xc3, 0x3b, 0x67, 0x9d, 0x78, 0xf2, 0x27, 0xf6, 0xfd, 0x16, 0x6, 0x94, 0x80, 0xce, 0x22, 0xd9, 0xe5, 0xc6, 0xbd, 0xc4, 0x6d, 0xcd, 0x3d, 0x90, 0xf9, 0x42, 0xc0, 0xdc, 0x5f, 0xe7, 0x21, 0x52, 0x7c, 0x0, 0xf1, 0x7e, 0x68, 0x4a, 0x1a, 0x1d, 0xa1, 0x14, 0xad, 0xac, 0x81, 0x79, 0xa6, 0xa0, 0xe3, 0x28, 0x64, 0x98, 0x8e, 0xba, 0x5e, 0xb9, 0xfd, 0x67, 0xf2, 0x9b, 0x90, 0x7f, 0x4c, 0x45, 0xc0, 0x31, 0x55, 0x1f, 0x15, 0x32, 0x19, 0x16, 0x68, 0x44, 0xa0, 0x65, 0x2d, 0xc9, 0x64, 0x29, 0xbd, 0x44, 0xc6, 0x2c, 0xea, 0x9f, 0x8e, 0x40, 0xcf, 0x49, 0x2a, 0xd4, 0x58, 0x99, 0xc5, 0x32, 0xfb, 0xe2, 0xfe, 0x4c, 0x60, 0xb6, 0x5b, 0xc7, 0x4f, 0x39, 0xc0, 0x7, 0xe6, 0xb8, 0xfc, 0x6b, 0xdf, 0x8e, 0x5a, 0x12, 0x6, 0xa3, 0x30, 0x8e, 0x4f, 0x93, 0x5, 0x61, 0x8, 0xda, 0x8, 0x86, 0x91, 0xa0, 0x92, 0x17, 0x51, 0x24, 0x7d, 0xff, 0xcf, 0x16, 0xc6, 0x18, 0xc1, 0x5e, 0xfb, 0xb9, 0x8e, 0x31, 0x27, 0xfb, 0x5f, 0x7b, 0x71, 0x64, 0x7b, 0xcf, 0x7b, 0xce, 0xf3, 0x3c, 0xdb, 0x65, 0x81, 0x2, 0xf4, 0x8, 0x5c, 0xc0, 0xec, 0xd8, 0x1, 0xf0, 0x23, 0xf0, 0x4b, 0xe8, 0x2, 0x46, 0x6f, 0x5b, 0xfc, 0x1e, 0x2f, 0x21, 0x8e, 0x21, 0xa, 0x78, 0x7c, 0xc6, 0xdf, 0xc7, 0x31, 0x44, 0x23, 0x62, 0x1, 0x1b, 0x8, 0x50, 0x68, 0x44, 0x6a, 0xc5, 0x2e, 0xa0, 0x21, 0xcf, 0xa8, 0x15, 0xfb, 0x32, 0xa, 0x15, 0xe0, 0xcb, 0xc8, 0xd7, 0x71, 0xa8, 0x0, 0x5f, 0xc7, 0x1e, 0x48, 0x42, 0x5, 0x78, 0x20, 0xf1, 0x48, 0x16, 0x2b, 0xc0, 0x23, 0x99, 0x87, 0x52, 0xf3, 0xfa, 0xcb, 0x22, 0xed, 0xa1, 0xd4, 0x63, 0x39, 0x99, 0x62, 0xe7, 0xc7, 0x58, 0xce, 0xc5, 0x84, 0x8c, 0x67, 0x75, 0x13, 0x6e, 0x1c, 0x1a, 0x2f, 0x26, 0x5e, 0xcd, 0xcc, 0x12, 0x77, 0x17, 0x56, 0x33, 0x2e, 0xa7, 0x66, 0xd, 0x3f, 0x6, 0xcb, 0x29, 0xd7, 0x73, 0x53, 0xce, 0xf3, 0x7c, 0x5e, 0x66, 0x0, 0xeb, 0x39, 0x4, 0x8a, 0x0, 0x10, 0x28, 0x2c, 0xd1, 0x84, 0xb0, 0x44, 0x63, 0x91, 0x2a, 0x86, 0x45, 0x2a, 0xcb, 0x74, 0x31, 0x2c, 0xd3, 0x59, 0xa8, 0xc, 0x61, 0xa1, 0xd2, 0x52, 0xad, 0xd8, 0xee, 0x17, 0x8b, 0xbd, 0xe, 0xac, 0xa5, 0xda, 0x9f, 0x14, 0x69, 0xb1, 0x3a, 0x4d, 0x99, 0x7f, 0x3f, 0x54, 0x9c, 0x43, 0x8b, 0xd5, 0x96, 0xeb, 0xd3, 0x3c, 0x8d, 0xaa, 0xa6, 0x85, 0x79, 0x10, 0x72, 0x3d, 0xc, 0xb, 0xdc, 0x6f, 0xbe, 0x3b, 0x6d, 0x58, 0xd8, 0xb2, 0x49, 0x73, 0xef, 0x1, 0xca, 0x96, 0x8d, 0x4d, 0x2b, 0x74, 0x2d, 0xf4, 0x16, 0x9b, 0x56, 0xb6, 0xed, 0x62, 0xf3, 0x80, 0x6d, 0x3b, 0x1b, 0x97, 0xc7, 0x18, 0xbf, 0xd7, 0x4b, 0x61, 0x5d, 0xa5, 0x8c, 0xcb, 0x88, 0x75, 0xdb, 0x64, 0x89, 0x86, 0x1, 0xeb, 0x36, 0x62, 0x5e, 0x57, 0xac, 0xb1, 0x49, 0xc2, 0xbc, 0x8e, 0xd8, 0xf7, 0x15, 0xf, 0x87, 0x79, 0xa0, 0xfe, 0x5f, 0xb4, 0xef, 0xfb, 0x13, 0x60, 0xe8, 0x3e, 0xc2, 0xd1, 0x7d, 0x88, 0xa5, 0xfb, 0x18, 0x4f, 0x22, 0xc8, 0xb4, 0x3a, 0x3d, 0xc8, 0xb4, 0x6a, 0x4, 0x99, 0xba, 0x8e, 0x72, 0x8d, 0xcf, 0x15, 0x66, 0xdb, 0xfd, 0x29, 0xcc, 0xb6, 0xb9, 0xeb, 0x67, 0x9c, 0xaf, 0x7d, 0xa0, 0xf1, 0x23, 0x1d, 0x68, 0xec, 0x73, 0xa4, 0xb3, 0x4e, 0xec, 0x4, 0x42, 0xad, 0x7d, 0x8f, 0xf5, 0x76, 0x1f, 0x6c, 0xbe, 0x80, 0x68, 0x77, 0xf7, 0xe1, 0xf6, 0x3, 0x93, 0xb6, 0xf1, 0xfe, 0x49, 0x76, 0x6e, 0x8a, 0xba, 0x35, 0x9b, 0x97, 0x22, 0xfb, 0xf, 0xa6, 0xb7, 0xf9, 0xe7, 0x9, 0xe4, 0xc9, 0x4f, 0x3c, 0xae, 0xe2, 0x23, 0x97, 0xb, 0xf8, 0xcc, 0xa7, 0xe2, 0x26, 0xfd, 0xa1, 0x53, 0x9b, 0x53, 0xd7, 0xf3, 0x4f, 0xbd, 0x6, 0x6, 0x6, 0x6, 0xae, 0x8a, 0x2f, 0x2e, 0x60, 0x3a, 0x41, 0x7b, 0x3f, 0x8e, 0x53, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon6Name[] = { 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x00 }; +const unsigned char defaultDialogIcon6Length[] = { 0x39, 0x33, 0x30, 0x00 }; +const unsigned char defaultDialogIcon6Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x9d, 0xb7, 0x81, 0xec, 0x0, 0x0, 0x0, 0x96, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0xb, 0xcd, 0x2a, 0x0, 0x0, 0x0, 0x31, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xf9, 0x1c, 0xe6, 0xb1, 0x4d, 0x3b, 0x17, 0xc9, 0xad, 0x49, 0x6, 0x8f, 0x40, 0xf6, 0xe3, 0xe0, 0xc1, 0x20, 0x11, 0x62, 0x54, 0x34, 0x30, 0x2b, 0x5d, 0xd6, 0xd0, 0x98, 0x76, 0x65, 0xb, 0x9b, 0x92, 0x6d, 0x2, 0x9, 0x3, 0x9c, 0xb5, 0x9d, 0xed, 0xe8, 0xbb, 0x79, 0xdc, 0x83, 0x71, 0x9f, 0x7a, 0xba, 0x9d, 0xd4, 0x0, 0x0, 0x2, 0x8a, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xec, 0x53, 0xdb, 0x62, 0x82, 0x30, 0xc, 0x5d, 0xcb, 0x5d, 0x51, 0x10, 0x10, 0x11, 0x1, 0x2f, 0xa8, 0x88, 0x9b, 0x3a, 0xfe, 0xff, 0xe7, 0xa6, 0x69, 0x9, 0x2d, 0x97, 0xa9, 0xf, 0x7b, 0x5b, 0x9e, 0x48, 0xd2, 0x13, 0x92, 0x73, 0x92, 0x8f, 0x7f, 0xfb, 0x53, 0xcb, 0x69, 0xa8, 0x5, 0x69, 0x1a, 0x68, 0x21, 0xcd, 0xdf, 0x47, 0x1b, 0x9a, 0x6d, 0x3a, 0xa4, 0x2, 0x23, 0x8e, 0x69, 0x6b, 0xc6, 0x5b, 0x70, 0x3d, 0x71, 0xab, 0x96, 0xb9, 0x89, 0xfe, 0x32, 0x7c, 0xa2, 0x92, 0xaa, 0xc7, 0x88, 0x3a, 0x79, 0x9, 0xee, 0x1d, 0x11, 0xde, 0x29, 0x71, 0xf4, 0x9e, 0xe3, 0x35, 0x57, 0x44, 0x28, 0x71, 0xac, 0x10, 0x71, 0x10, 0xed, 0x19, 0x7e, 0x37, 0xad, 0xdf, 0x2a, 0x56, 0xa6, 0x47, 0xd4, 0xf7, 0x69, 0xa4, 0x67, 0x96, 0x52, 0x47, 0xa7, 0xbb, 0x5f, 0xe1, 0xeb, 0x2d, 0xce, 0x3b, 0xa7, 0x62, 0x82, 0xce, 0x91, 0x97, 0xed, 0x7a, 0x18, 0xbf, 0xb7, 0xf8, 0xa3, 0xd9, 0xa8, 0x9b, 0x1c, 0xcd, 0x78, 0xd2, 0xda, 0xf, 0x16, 0xd8, 0xf2, 0xe6, 0x97, 0xe3, 0xbe, 0xec, 0x78, 0xa9, 0xf0, 0x1e, 0x6, 0xe7, 0x67, 0xf9, 0xd5, 0x62, 0xe8, 0xc1, 0x62, 0xc5, 0x5e, 0xc, 0xf0, 0x30, 0x62, 0xfc, 0xa9, 0x46, 0x3, 0x48, 0x2d, 0xd3, 0x54, 0xbf, 0x9a, 0x82, 0x86, 0xca, 0x98, 0x1c, 0xf5, 0xea, 0xef, 0x30, 0x3c, 0x72, 0x44, 0x37, 0x53, 0xce, 0xfc, 0xb7, 0x87, 0x2c, 0xb3, 0xa, 0x4e, 0xdf, 0x3e, 0x9c, 0x58, 0xff, 0xf8, 0xff, 0x30, 0xae, 0xd0, 0x2e, 0x3a, 0x56, 0x65, 0x53, 0x9c, 0x7a, 0xf6, 0x97, 0x0, 0x7f, 0xd8, 0x6e, 0xe4, 0x20, 0x5c, 0x8a, 0x2f, 0x80, 0x49, 0xd2, 0xd9, 0xea, 0x3, 0xeb, 0x6d, 0x89, 0x94, 0x33, 0xd1, 0xd0, 0x4c, 0xd4, 0x6e, 0xc9, 0x26, 0x3d, 0xb4, 0xef, 0x8f, 0x80, 0xfe, 0xa8, 0xdf, 0x9c, 0x2f, 0x84, 0x6d, 0xf3, 0x4a, 0x1, 0x2e, 0xb, 0x4, 0x48, 0xfb, 0x36, 0x13, 0x88, 0x36, 0xec, 0x16, 0xe0, 0xdf, 0xee, 0x5, 0xc7, 0x37, 0x56, 0x3b, 0x47, 0xb5, 0xc0, 0x4f, 0x64, 0xbc, 0xe1, 0x42, 0x5f, 0xe8, 0xd3, 0xf2, 0xe1, 0x6f, 0x98, 0xb3, 0x79, 0x7c, 0x97, 0x48, 0x7c, 0xe, 0xd3, 0xba, 0x86, 0x7c, 0x83, 0xd0, 0xe6, 0x1c, 0xfd, 0x10, 0x14, 0x9c, 0x20, 0xbf, 0xd8, 0x73, 0x33, 0x9e, 0x7c, 0x97, 0x36, 0x50, 0x4d, 0x65, 0x4d, 0x4a, 0xfe, 0x13, 0xf, 0xaa, 0x35, 0xe3, 0x51, 0x10, 0xc2, 0x96, 0xd6, 0xdc, 0x84, 0x2b, 0x11, 0x46, 0xba, 0xdc, 0xfd, 0x62, 0xcc, 0x87, 0x6e, 0xb3, 0x66, 0x81, 0x2e, 0xe2, 0xc1, 0x50, 0x10, 0x3d, 0x13, 0xd7, 0xc2, 0x2c, 0xb, 0x8f, 0x2b, 0xc, 0x84, 0x9e, 0x85, 0x99, 0x33, 0xd8, 0x46, 0xf1, 0xde, 0x43, 0x20, 0x56, 0x97, 0x9a, 0xf2, 0xf3, 0x7a, 0xa3, 0xd9, 0x11, 0x8b, 0x9a, 0x43, 0x4b, 0x61, 0x9b, 0x43, 0x12, 0xa1, 0x8f, 0x16, 0x5d, 0x8b, 0x33, 0xbb, 0x87, 0x50, 0x8c, 0x92, 0x36, 0x8b, 0x1, 0x72, 0xd8, 0xc2, 0x3, 0x5d, 0xb8, 0xfc, 0x32, 0x8b, 0x81, 0x10, 0x49, 0x1f, 0x81, 0xd8, 0xef, 0x14, 0xb8, 0xd6, 0xf8, 0x4f, 0x49, 0x75, 0x1f, 0xee, 0x2c, 0x7d, 0xa1, 0xc0, 0x4f, 0xfb, 0x65, 0xaf, 0x2, 0x20, 0xc, 0xc4, 0xe0, 0xb5, 0xae, 0xfe, 0xc, 0x22, 0x88, 0x8b, 0xe, 0xda, 0xf7, 0x7f, 0x3e, 0xb5, 0x20, 0xd1, 0x7e, 0x48, 0x87, 0xe0, 0x66, 0x36, 0x15, 0x5a, 0x9b, 0xde, 0x25, 0xb9, 0xe5, 0x52, 0xb1, 0xf4, 0x9, 0xb, 0x94, 0x8f, 0x30, 0x25, 0x6e, 0x86, 0x51, 0x37, 0x86, 0x23, 0x80, 0x44, 0x2e, 0x50, 0x49, 0x86, 0x41, 0x22, 0xae, 0x11, 0xb, 0xa0, 0x6d, 0x70, 0x8d, 0x28, 0x24, 0x70, 0xb0, 0xe8, 0x99, 0x85, 0xc4, 0x52, 0x46, 0x8b, 0xa8, 0xe8, 0x51, 0xca, 0x6c, 0x26, 0x8, 0xb5, 0xba, 0x48, 0x40, 0x33, 0xb1, 0x9d, 0x85, 0x36, 0x4, 0x91, 0xcd, 0x76, 0xa6, 0xa0, 0x0, 0x73, 0xa6, 0x7d, 0x10, 0x14, 0x48, 0x5a, 0x11, 0x94, 0x34, 0x8a, 0xaa, 0xd0, 0xf4, 0x7d, 0x5e, 0x9f, 0x14, 0x55, 0xca, 0xba, 0xe8, 0xa, 0x7, 0xdb, 0x19, 0xb5, 0x90, 0x75, 0x1a, 0x8b, 0x10, 0xcf, 0x77, 0xf1, 0x69, 0xb0, 0x2b, 0x8d, 0x85, 0xd6, 0xc6, 0x3a, 0x80, 0xb5, 0x15, 0xcc, 0x55, 0xbf, 0xa5, 0xcd, 0x60, 0xae, 0xef, 0xf6, 0x2e, 0x74, 0xdb, 0xd6, 0xdd, 0xf7, 0x97, 0xbd, 0x7b, 0x1, 0xc3, 0x8d, 0x38, 0x76, 0xc8, 0x72, 0x63, 0x9e, 0x19, 0x34, 0xdd, 0xa8, 0xeb, 0x85, 0xed, 0xa9, 0x36, 0xe3, 0xbe, 0x39, 0x70, 0x98, 0x23, 0x8f, 0x39, 0x74, 0xfd, 0xf8, 0x12, 0x3b, 0xca, 0x8, 0xb2, 0x9d, 0xe6, 0x23, 0xa2, 0x27, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon7Name[] = { 0x71, 0x75, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x32, 0x78, 0x00 }; +const unsigned char defaultDialogIcon7Length[] = { 0x31, 0x35, 0x33, 0x38, 0x00 }; +const unsigned char defaultDialogIcon7Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf4, 0xe0, 0x91, 0xf9, 0x0, 0x0, 0x0, 0x9c, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf8, 0x8d, 0x4c, 0xde, 0x0, 0x0, 0x0, 0x33, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xfb, 0x42, 0x2, 0xa, 0x62, 0xb7, 0x82, 0xe6, 0x95, 0xe, 0x5, 0xb2, 0x19, 0x2b, 0xef, 0xe2, 0x3b, 0xd9, 0x4e, 0xc6, 0xf6, 0xbe, 0x55, 0x48, 0x91, 0x67, 0x33, 0x6c, 0x27, 0x23, 0xc2, 0xba, 0x20, 0x14, 0xd4, 0xa8, 0x89, 0x71, 0x38, 0xea, 0xd1, 0xcb, 0x8c, 0x7b, 0x52, 0x9c, 0xde, 0xa3, 0x88, 0x5c, 0x25, 0x3a, 0xec, 0xc2, 0x0, 0x0, 0x4, 0xe2, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x97, 0xdb, 0x76, 0xb2, 0x30, 0x10, 0x85, 0x87, 0x70, 0x52, 0x11, 0x1, 0x1, 0x6b, 0x55, 0xea, 0x59, 0xeb, 0xa1, 0x6a, 0x3b, 0xef, 0xff, 0x6e, 0xff, 0xc5, 0xbf, 0x96, 0x24, 0x30, 0x61, 0x0, 0x6d, 0xaf, 0xfc, 0x6e, 0x61, 0x85, 0x64, 0xcf, 0xec, 0xd9, 0x1, 0x5e, 0xbc, 0x78, 0xf1, 0xe2, 0x45, 0x7b, 0x2c, 0x3f, 0x9b, 0x8a, 0x38, 0x16, 0xd3, 0xcc, 0xb7, 0xe0, 0x6f, 0xe9, 0x89, 0x59, 0x77, 0x11, 0xa2, 0x44, 0xb8, 0xe8, 0xce, 0x44, 0xf, 0xfe, 0x2, 0x4f, 0xb8, 0x89, 0x81, 0x24, 0x46, 0xe2, 0xa, 0xf, 0x7e, 0x15, 0x73, 0xbc, 0x49, 0xb1, 0x92, 0x74, 0x33, 0x36, 0xe1, 0xb7, 0x18, 0x9c, 0x3a, 0x58, 0x83, 0xce, 0x69, 0x0, 0xbf, 0x41, 0xe4, 0x60, 0x6d, 0x9c, 0x8, 0x9e, 0x8d, 0x18, 0x61, 0x23, 0x46, 0xe2, 0xb9, 0xe2, 0x8f, 0xb0, 0x31, 0xa3, 0xe7, 0x15, 0xc2, 0xda, 0x1a, 0xd8, 0x2, 0x63, 0x6b, 0xc1, 0x53, 0x88, 0x43, 0x6c, 0x49, 0x18, 0x3f, 0xc3, 0x79, 0x27, 0x7c, 0x80, 0xed, 0xc3, 0x9e, 0xec, 0xf5, 0xb5, 0xa, 0x87, 0xc7, 0x7e, 0xf7, 0x6c, 0xdb, 0xe7, 0x6e, 0xff, 0x18, 0x6a, 0x6b, 0xd4, 0x7f, 0x70, 0x3c, 0x7e, 0xac, 0x91, 0xe2, 0xe0, 0xee, 0x7d, 0x53, 0xd1, 0xc9, 0xdf, 0xbb, 0x7, 0xa4, 0x58, 0x7f, 0x3c, 0x64, 0xbe, 0x4f, 0xe2, 0xe4, 0xf3, 0xdd, 0x12, 0x48, 0x96, 0xbb, 0x39, 0xa1, 0xc4, 0xa7, 0x80, 0xd6, 0x4c, 0xca, 0xeb, 0x39, 0xf6, 0xa, 0x2a, 0x58, 0xd9, 0x4e, 0x79, 0xc7, 0x93, 0xb6, 0xb1, 0xd3, 0xc5, 0x22, 0x49, 0x4, 0x2c, 0x51, 0x82, 0x45, 0xde, 0xbd, 0x56, 0xdf, 0x2f, 0x9d, 0x65, 0xf8, 0x5, 0xb5, 0xf8, 0x1a, 0x96, 0x74, 0x6b, 0xb3, 0x83, 0xe2, 0xf9, 0x3b, 0x93, 0xda, 0xab, 0x78, 0x93, 0x4e, 0x51, 0x83, 0x16, 0xf5, 0x2f, 0x14, 0xd2, 0xb5, 0xa0, 0x1, 0x96, 0x6b, 0xa0, 0x42, 0xe3, 0x3e, 0x10, 0xea, 0x2, 0x81, 0x68, 0xbc, 0x40, 0xa0, 0x1e, 0x40, 0x34, 0xf4, 0xbf, 0xea, 0xbf, 0x61, 0xb, 0x33, 0x7f, 0xc, 0x55, 0x37, 0x2e, 0x1b, 0xcd, 0xbf, 0xb5, 0xda, 0x43, 0xbd, 0x56, 0x43, 0xd4, 0x51, 0x27, 0x52, 0x83, 0x45, 0x4c, 0x75, 0xfe, 0x9e, 0x4c, 0x8d, 0xe7, 0x6f, 0xa3, 0x61, 0x9a, 0x5e, 0xe7, 0x9b, 0x99, 0x4f, 0x2f, 0xf3, 0xae, 0x4e, 0xe5, 0xfa, 0xb9, 0xb0, 0x55, 0xaa, 0x67, 0x93, 0x8b, 0xdb, 0xca, 0xd4, 0x3b, 0xce, 0x48, 0x8b, 0xd8, 0x86, 0x92, 0x4c, 0xb5, 0xf3, 0x17, 0x65, 0xc8, 0xef, 0x8f, 0x4b, 0x19, 0x71, 0x7d, 0x23, 0x77, 0x80, 0x32, 0x71, 0x4d, 0xb, 0x85, 0x94, 0x83, 0xf9, 0x88, 0x9c, 0x53, 0x85, 0x50, 0xaa, 0x10, 0x5a, 0xcd, 0xb, 0xe0, 0x98, 0xb5, 0x23, 0x12, 0xc3, 0x29, 0x51, 0x2a, 0xa5, 0x13, 0xbf, 0xa1, 0x6, 0x3, 0x43, 0xf6, 0x1f, 0xd1, 0xba, 0xd3, 0x0, 0x35, 0xa4, 0x82, 0x50, 0x4b, 0x76, 0xa3, 0x31, 0x0, 0x9e, 0x11, 0xe6, 0x4, 0x84, 0xff, 0xfd, 0xe, 0x6a, 0x9, 0x32, 0x42, 0x2f, 0x79, 0xbf, 0x23, 0x60, 0x11, 0xcc, 0xf8, 0xb2, 0x8e, 0x58, 0xc1, 0x70, 0xc5, 0xc, 0x55, 0xd1, 0x48, 0x0, 0x97, 0xc9, 0x28, 0x82, 0xd, 0x94, 0x71, 0x9b, 0x48, 0x10, 0xc9, 0xf9, 0x47, 0x34, 0x6d, 0x66, 0x60, 0x35, 0x7b, 0x42, 0x34, 0x79, 0xae, 0x47, 0x50, 0x8d, 0xc3, 0x24, 0x58, 0xc1, 0x80, 0x69, 0x92, 0xa4, 0x5, 0x33, 0x42, 0x99, 0x99, 0x6c, 0x2b, 0xc6, 0x2, 0x72, 0x3d, 0x3d, 0xc2, 0x1, 0x6a, 0xca, 0xf, 0x4c, 0x0, 0x73, 0x20, 0x79, 0x9d, 0xae, 0xb2, 0x27, 0x3b, 0xa1, 0xda, 0x8, 0x27, 0xcc, 0xa1, 0xee, 0x3f, 0xdf, 0xf2, 0xec, 0xbb, 0x7f, 0x4a, 0x5c, 0x31, 0xe7, 0x42, 0xdd, 0x91, 0xe4, 0x64, 0xa9, 0x4c, 0x21, 0xc9, 0x62, 0x9, 0x10, 0x48, 0x47, 0x9, 0x7c, 0xc9, 0x9a, 0x81, 0xd4, 0x39, 0x54, 0xe6, 0x24, 0xc4, 0x73, 0x8a, 0x31, 0xd3, 0x2d, 0x19, 0xe6, 0xfc, 0x80, 0xc4, 0x8f, 0xdc, 0x86, 0x4c, 0x6f, 0x8f, 0x41, 0xcf, 0x86, 0x69, 0x96, 0x37, 0xed, 0xf3, 0x5, 0x73, 0xfb, 0x72, 0x64, 0xa7, 0x6a, 0xf1, 0x52, 0x26, 0x4, 0x77, 0x78, 0x67, 0xa7, 0x7d, 0xe2, 0x32, 0xb1, 0x98, 0x7a, 0x75, 0xa6, 0xa0, 0xb1, 0x62, 0x7a, 0x34, 0xd2, 0x6a, 0x7c, 0x1, 0x82, 0x95, 0x51, 0x67, 0x1a, 0xba, 0xb2, 0x9d, 0x29, 0x6e, 0xf9, 0x6, 0xad, 0x42, 0xe6, 0x48, 0x37, 0x1f, 0xa0, 0x98, 0x93, 0x12, 0xe9, 0x7b, 0x75, 0xc7, 0xec, 0x70, 0xad, 0x1f, 0x20, 0x1b, 0x60, 0xaa, 0x97, 0x80, 0x86, 0x9e, 0x24, 0xd3, 0x92, 0x76, 0x89, 0x56, 0x67, 0x9b, 0xfb, 0x9, 0x59, 0xa2, 0xa4, 0x1e, 0xdf, 0x2, 0x7, 0xc8, 0xa1, 0x9a, 0x3d, 0x58, 0xea, 0x33, 0xea, 0xc, 0x24, 0x7, 0xbe, 0x9, 0x66, 0x7c, 0x99, 0xfc, 0xff, 0xcb, 0x4, 0x31, 0xa8, 0x64, 0xa9, 0xb4, 0x3c, 0xdb, 0x60, 0x33, 0xfe, 0x6f, 0x70, 0xaf, 0x75, 0xea, 0x79, 0x1e, 0xac, 0x2f, 0xc5, 0xf3, 0x9b, 0x47, 0x5e, 0xe0, 0x3d, 0xde, 0xe9, 0x6a, 0xf5, 0xbd, 0xe3, 0x43, 0x13, 0xac, 0x1b, 0xe6, 0x2c, 0x74, 0xe2, 0xf1, 0xaf, 0x84, 0xf9, 0x29, 0x4c, 0x68, 0x40, 0x74, 0x45, 0xe4, 0xb, 0x6c, 0xe6, 0x2d, 0x1e, 0x6a, 0xce, 0x81, 0xc4, 0x1b, 0xc, 0x56, 0xb4, 0xbb, 0xd, 0x51, 0x66, 0x1, 0x3a, 0xa4, 0xa0, 0xb3, 0x38, 0x8d, 0x8e, 0x50, 0x8b, 0xde, 0xc5, 0xc0, 0x22, 0x53, 0xd0, 0x71, 0xe4, 0x2a, 0x9c, 0xa9, 0xc3, 0x8c, 0xa7, 0x77, 0xe5, 0xef, 0x84, 0xf4, 0x65, 0x2a, 0x3, 0x8a, 0xa9, 0xda, 0xa6, 0x3c, 0x17, 0x2c, 0x61, 0x64, 0xa0, 0xa5, 0xcb, 0xc9, 0x24, 0x94, 0x59, 0xc2, 0x63, 0x95, 0xf5, 0x4f, 0xab, 0xb2, 0xfe, 0xcc, 0x35, 0x6a, 0xac, 0x64, 0x31, 0x4f, 0x54, 0xf1, 0x67, 0xc4, 0x24, 0x72, 0xfc, 0x94, 0xd, 0xbc, 0x61, 0x81, 0xe0, 0x5f, 0xfb, 0x76, 0xb3, 0x9a, 0x30, 0x14, 0x44, 0x1, 0xb8, 0x12, 0x17, 0x37, 0x20, 0x95, 0x2e, 0xa, 0xa6, 0x75, 0xd1, 0xa, 0x5, 0x37, 0x95, 0xd6, 0xf7, 0x7f, 0xb8, 0x62, 0x11, 0x5, 0xaf, 0xf1, 0x53, 0xc7, 0x9f, 0x1b, 0xc9, 0x59, 0x67, 0x31, 0x9a, 0xcc, 0xdf, 0x39, 0x67, 0x5e, 0x9e, 0x2, 0x1, 0xe8, 0x15, 0x38, 0x80, 0x71, 0x5b, 0xf9, 0xf4, 0x2b, 0xf0, 0x47, 0xe8, 0x0, 0x6, 0x8b, 0x19, 0x9e, 0xc7, 0x47, 0x88, 0x34, 0x44, 0x0, 0xaf, 0x3f, 0x6d, 0xf9, 0xef, 0x34, 0x74, 0x21, 0x72, 0x0, 0xd, 0x8, 0x28, 0x14, 0x22, 0x95, 0x62, 0x7, 0x90, 0xd1, 0x33, 0x2a, 0xc5, 0x6e, 0x46, 0xa1, 0x0, 0xdc, 0x8c, 0xdc, 0x8e, 0x43, 0x1, 0xb8, 0x1d, 0x7b, 0x20, 0x9, 0x5, 0xe0, 0x81, 0xc4, 0x23, 0x59, 0x2c, 0x0, 0x8f, 0x64, 0x1e, 0x4a, 0x8d, 0xef, 0x3, 0x8b, 0xb4, 0x87, 0x52, 0x8f, 0xe5, 0xc4, 0x33, 0xf2, 0x1a, 0x63, 0x39, 0x17, 0x13, 0xa2, 0x1a, 0x6f, 0x8a, 0x70, 0x96, 0x34, 0x5e, 0x4c, 0xbc, 0x9a, 0x19, 0x6f, 0xe8, 0x5d, 0x58, 0xcd, 0xb8, 0x9c, 0x1a, 0x35, 0x3a, 0x7, 0x96, 0x53, 0xae, 0xe7, 0xc6, 0x64, 0x94, 0xd2, 0x68, 0x82, 0x87, 0xb0, 0x9e, 0x83, 0xa0, 0x8, 0x40, 0x4, 0x85, 0x29, 0x9a, 0x10, 0x4c, 0xd1, 0x98, 0xa4, 0x8a, 0xc1, 0x24, 0x95, 0x69, 0xba, 0x18, 0x4c, 0xd3, 0x99, 0xa8, 0xc, 0xc1, 0x44, 0xa5, 0xa9, 0x5a, 0x61, 0xb6, 0xac, 0xeb, 0x25, 0x12, 0x16, 0x54, 0x2d, 0xc8, 0x6a, 0x64, 0x61, 0xfa, 0x7f, 0x4c, 0x79, 0x38, 0x9c, 0x83, 0x45, 0x4, 0x5d, 0xdf, 0x8a, 0xf7, 0xc1, 0xba, 0x68, 0x61, 0x1e, 0x4, 0x5d, 0xf, 0xc1, 0x2, 0xfd, 0xd, 0xbd, 0x53, 0x82, 0x85, 0x25, 0x1b, 0x8c, 0x38, 0x18, 0xa0, 0x2c, 0xd9, 0x58, 0xb4, 0x42, 0xd5, 0x42, 0x6d, 0xb1, 0x68, 0x65, 0xd9, 0xe, 0x29, 0x8b, 0xd4, 0xa2, 0x6c, 0x67, 0xe1, 0x32, 0x36, 0xf, 0x58, 0xb8, 0xb4, 0x74, 0x8b, 0x79, 0x20, 0x2b, 0x18, 0x90, 0x6e, 0x3, 0xe2, 0x75, 0x86, 0x45, 0xc6, 0x9a, 0x42, 0xbc, 0x3e, 0x53, 0xbe, 0x6f, 0xaa, 0xd6, 0xff, 0x6a, 0x35, 0xf, 0x64, 0xbf, 0xb, 0xf2, 0xfd, 0xed, 0xc, 0xc, 0x1f, 0x3b, 0x6, 0x86, 0x2e, 0x59, 0x38, 0xee, 0x6f, 0x62, 0xb9, 0xbf, 0x8d, 0x67, 0x8f, 0x91, 0x69, 0x7a, 0xbc, 0x91, 0x69, 0x3a, 0x87, 0x91, 0xe9, 0x6, 0x56, 0x2e, 0x27, 0x80, 0x23, 0x68, 0x2e, 0x67, 0x66, 0xeb, 0xa6, 0x9d, 0xaf, 0x0, 0x43, 0xe3, 0x45, 0x2c, 0x9d, 0x9f, 0x57, 0x33, 0xb5, 0xa6, 0xad, 0xa9, 0x35, 0xc1, 0xd4, 0x1a, 0x41, 0xf5, 0x15, 0xb3, 0xf5, 0x76, 0xdf, 0xd8, 0x1c, 0xb0, 0x76, 0xff, 0xae, 0x8a, 0xe7, 0x43, 0x98, 0xdb, 0xb, 0xb0, 0xf7, 0x17, 0x70, 0xe0, 0x50, 0xc0, 0x89, 0x47, 0x1, 0x47, 0x2e, 0x5, 0x9c, 0xf9, 0xac, 0x31, 0xdc, 0x7f, 0xe8, 0x74, 0x42, 0xd6, 0x75, 0xfd, 0xd4, 0xab, 0x47, 0x8f, 0x1e, 0x3d, 0x1e, 0xa, 0x7f, 0x3b, 0x1f, 0x29, 0xf, 0xbf, 0x29, 0x8c, 0x32, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon8Name[] = { 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x61, 0x72, 0x6b, 0x00 }; +const unsigned char defaultDialogIcon8Length[] = { 0x38, 0x33, 0x30, 0x00 }; +const unsigned char defaultDialogIcon8Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x9d, 0xb7, 0x81, 0xec, 0x0, 0x0, 0x0, 0x78, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xa8, 0xe4, 0xac, 0x0, 0x0, 0x0, 0x27, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x3, 0xfb, 0xa5, 0x7e, 0xc9, 0x8e, 0xeb, 0xb2, 0x1c, 0x13, 0xc, 0x7b, 0xa4, 0x43, 0x92, 0xf6, 0xe1, 0xa0, 0x77, 0x47, 0x25, 0x8, 0xc5, 0xf2, 0xdc, 0xd6, 0xd4, 0x88, 0x6b, 0x51, 0x29, 0x27, 0xe, 0xbe, 0x37, 0x36, 0xbf, 0x58, 0xbe, 0xe, 0x1c, 0x70, 0x0, 0x0, 0x2, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xdc, 0x54, 0x6b, 0x8f, 0x82, 0x40, 0xc, 0x64, 0x91, 0xe5, 0xa5, 0x82, 0xf2, 0x46, 0x5e, 0xea, 0x99, 0xf4, 0xff, 0xff, 0xc3, 0x4b, 0xbb, 0x1e, 0x50, 0xea, 0x5, 0x3c, 0xbf, 0x5d, 0xa3, 0x89, 0x3b, 0x9d, 0x19, 0xda, 0x6e, 0xd1, 0xfa, 0xdf, 0xa1, 0xc6, 0xf8, 0x93, 0x58, 0x9c, 0xdf, 0x9, 0x62, 0x7, 0x59, 0xd7, 0xc6, 0x49, 0x12, 0xb7, 0x5d, 0x16, 0x10, 0xf8, 0x8e, 0x5c, 0x45, 0xae, 0x3e, 0xc1, 0x18, 0x27, 0xed, 0x46, 0x88, 0x6e, 0x95, 0x87, 0x69, 0x43, 0x3a, 0xfb, 0x19, 0x74, 0x68, 0xd2, 0x90, 0x92, 0xeb, 0xfa, 0xbd, 0xe3, 0x91, 0x78, 0x74, 0xf8, 0x39, 0x79, 0xce, 0x7e, 0xcd, 0x1, 0xf3, 0x7d, 0x4d, 0x7c, 0xfa, 0x9a, 0x98, 0xce, 0x75, 0x2f, 0x8a, 0x10, 0x7a, 0x17, 0x0, 0x95, 0xa4, 0xae, 0xce, 0xda, 0xf7, 0xf5, 0xb9, 0x2, 0x18, 0x41, 0x97, 0x3b, 0xc8, 0xf2, 0x7d, 0x7a, 0x12, 0x7e, 0xca, 0xc3, 0x23, 0x2c, 0x70, 0x72, 0x45, 0xf8, 0x38, 0x94, 0x4, 0x62, 0xce, 0x9f, 0xb5, 0x21, 0xf5, 0xd7, 0xb, 0xd1, 0xb0, 0xdd, 0x9c, 0xe7, 0x72, 0x1c, 0xc, 0xe5, 0x2e, 0xd7, 0xc9, 0x41, 0xe8, 0x3d, 0xc3, 0xb1, 0xdd, 0x80, 0xed, 0xe, 0xfd, 0xa, 0x5c, 0xdb, 0x64, 0xbd, 0xd1, 0x41, 0xd4, 0xff, 0x7c, 0x7e, 0x39, 0xbc, 0xde, 0xc4, 0x41, 0x3f, 0x6b, 0xf8, 0xad, 0xb, 0xdf, 0x8c, 0xee, 0xa8, 0x26, 0x39, 0xb7, 0x50, 0x47, 0x33, 0x4e, 0xff, 0x75, 0x1, 0xae, 0xd1, 0x3b, 0x6c, 0xce, 0x93, 0x15, 0xc1, 0x8e, 0x71, 0x70, 0x2d, 0x25, 0xf5, 0xbd, 0xa9, 0xcf, 0x61, 0x1a, 0x51, 0x84, 0x63, 0x58, 0xbd, 0x74, 0xd8, 0xd7, 0x80, 0x43, 0x3a, 0xf2, 0x8c, 0xca, 0x32, 0x7e, 0xb6, 0x8e, 0x44, 0xab, 0xf7, 0xa2, 0x0, 0x87, 0x12, 0x5a, 0x31, 0x83, 0xdb, 0x1d, 0xe0, 0x7e, 0x63, 0x3c, 0xa5, 0x89, 0xe8, 0x2c, 0x4b, 0x8, 0xcd, 0xd, 0xe, 0x88, 0x4f, 0xec, 0x3, 0xae, 0xce, 0x81, 0x63, 0x83, 0xb9, 0xcb, 0x70, 0x51, 0x40, 0xa, 0xb6, 0x18, 0xe, 0xd5, 0x8b, 0x5d, 0x71, 0x30, 0x26, 0x6a, 0xba, 0xe8, 0xb5, 0x41, 0xaa, 0x17, 0x2c, 0xb8, 0x3b, 0x44, 0x77, 0xb, 0x30, 0xf0, 0x10, 0x6d, 0x38, 0x18, 0x1, 0x4c, 0x8d, 0x49, 0x3, 0x39, 0x2e, 0x80, 0x8, 0x61, 0xbe, 0x3, 0x76, 0xbe, 0xcd, 0x20, 0xb7, 0xe5, 0x2e, 0x68, 0x64, 0x96, 0xd3, 0x59, 0x18, 0xb0, 0x28, 0x11, 0xd6, 0x73, 0x24, 0x38, 0x1, 0xf0, 0x71, 0x4b, 0x3, 0x7e, 0x39, 0x0, 0xa7, 0x60, 0x86, 0x64, 0x80, 0xf1, 0xd8, 0x6a, 0xf0, 0x45, 0xf4, 0x8c, 0x70, 0x83, 0x74, 0x68, 0x59, 0x85, 0x5b, 0x5b, 0x8, 0x2b, 0x2c, 0xb8, 0x9b, 0x19, 0xb4, 0x48, 0x3c, 0x17, 0x5b, 0xd, 0x8a, 0x33, 0xe2, 0xed, 0xcc, 0x20, 0xa6, 0xa9, 0xa8, 0xad, 0x6, 0x8a, 0x66, 0x1e, 0xcf, 0xc, 0x12, 0x4, 0x7c, 0x6b, 0xab, 0x81, 0xe5, 0x23, 0x9e, 0x7c, 0x6c, 0xf0, 0x71, 0xb, 0xeb, 0x43, 0x94, 0x2f, 0x93, 0x1c, 0xa2, 0xbc, 0x46, 0xf9, 0x3a, 0xaf, 0x5c, 0xa3, 0x5c, 0x24, 0xf9, 0x87, 0xb2, 0xb2, 0x48, 0x72, 0x95, 0x27, 0x72, 0xf4, 0xdd, 0x7d, 0xb9, 0xa3, 0x0, 0xc, 0xc3, 0x30, 0x74, 0xea, 0x9a, 0xe6, 0x73, 0x84, 0x90, 0xf8, 0xfe, 0x37, 0x6c, 0x31, 0x18, 0xf, 0xa2, 0x8, 0xaa, 0x2d, 0x86, 0x40, 0x8, 0x64, 0x31, 0xb6, 0xf4, 0x54, 0xf2, 0xd, 0x47, 0x19, 0x97, 0x9, 0x55, 0xd4, 0xf, 0x5b, 0x26, 0x5c, 0xe7, 0xec, 0x97, 0x77, 0x96, 0xac, 0x33, 0x8, 0x4a, 0xd6, 0x68, 0x6d, 0xc4, 0x1d, 0x5, 0x85, 0x4b, 0xda, 0xb6, 0xb7, 0xf6, 0xa7, 0xa4, 0x71, 0x51, 0xed, 0x56, 0xab, 0x75, 0x22, 0xaa, 0x8a, 0xac, 0x73, 0x63, 0x59, 0x66, 0x8b, 0x18, 0xb, 0xb1, 0xb6, 0x52, 0x88, 0xb5, 0xc9, 0xe6, 0xaa, 0xdb, 0xbb, 0xe, 0x18, 0x88, 0x38, 0xd7, 0x2f, 0xc4, 0xe1, 0x90, 0x75, 0x7, 0x64, 0xf9, 0xc4, 0xb, 0x98, 0x17, 0xff, 0x39, 0x68, 0xce, 0x0, 0xcd, 0x9, 0xa0, 0xa9, 0xa1, 0xae, 0xa, 0xdb, 0x22, 0xee, 0xab, 0x81, 0x43, 0x8c, 0x3c, 0x62, 0xe8, 0x12, 0x63, 0x9f, 0x16, 0x3c, 0x8f, 0xae, 0x7, 0x37, 0xe7, 0x62, 0x42, 0xa9, 0x5c, 0xc2, 0xf9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon9Name[] = { 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x64, 0x61, 0x72, 0x6b, 0x32, 0x78, 0x00 }; +const unsigned char defaultDialogIcon9Length[] = { 0x31, 0x32, 0x37, 0x34, 0x00 }; +const unsigned char defaultDialogIcon9Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf4, 0xe0, 0x91, 0xf9, 0x0, 0x0, 0x0, 0x96, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x25, 0xc5, 0xa8, 0x0, 0x0, 0x0, 0x31, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x55, 0xfb, 0x3, 0xe3, 0xc, 0x43, 0x92, 0x8a, 0xd7, 0xb7, 0x82, 0x62, 0x40, 0x19, 0x48, 0x4e, 0x2b, 0xee, 0xe7, 0x39, 0xbf, 0x21, 0x97, 0x67, 0xb4, 0x33, 0xd0, 0x6c, 0x9, 0xcb, 0xf, 0xc5, 0xb1, 0xf6, 0xc2, 0xbc, 0xa8, 0x71, 0x26, 0x25, 0x14, 0x7, 0xc6, 0x3b, 0x7c, 0x5d, 0x11, 0x7b, 0x95, 0x98, 0x75, 0x67, 0x0, 0x0, 0x3, 0xe2, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x96, 0xd9, 0x8e, 0xb3, 0x30, 0xc, 0x85, 0x5d, 0x2, 0x94, 0xb2, 0xb7, 0x65, 0x69, 0xe9, 0xbe, 0x4f, 0xb7, 0x91, 0xf2, 0xfe, 0x2f, 0xf7, 0x4b, 0xbf, 0x46, 0xc2, 0x50, 0x1a, 0x13, 0x12, 0x46, 0x73, 0xd1, 0xef, 0xda, 0xa2, 0x27, 0x3e, 0x3e, 0x76, 0xe1, 0xc3, 0x87, 0xf, 0x1f, 0x3e, 0x74, 0x67, 0x15, 0x5e, 0xa, 0x3b, 0x8e, 0xed, 0xe2, 0x12, 0xae, 0xe0, 0x77, 0xb1, 0xec, 0x65, 0x7e, 0x34, 0x39, 0xc2, 0x3c, 0xe6, 0x4b, 0xdb, 0x82, 0xdf, 0xe0, 0x16, 0x78, 0x77, 0x83, 0x37, 0x62, 0xdc, 0xbd, 0xe0, 0x6, 0xbd, 0xc2, 0xa2, 0xd3, 0x9e, 0xb, 0xd9, 0x9f, 0x22, 0x6, 0x7d, 0x31, 0x76, 0xa6, 0xbc, 0x5, 0x53, 0x67, 0xc, 0x7d, 0xf0, 0x70, 0x79, 0x6b, 0xdc, 0x4, 0x74, 0x63, 0xa7, 0x5c, 0x8a, 0xd4, 0xd6, 0xdb, 0xfc, 0x94, 0x4b, 0x93, 0xea, 0x33, 0xc2, 0x72, 0xc, 0xde, 0x1, 0xc3, 0xd1, 0x94, 0xcb, 0xd8, 0xe4, 0x1d, 0x31, 0x63, 0x50, 0x87, 0x39, 0x5c, 0x1, 0x87, 0x29, 0xb7, 0x7f, 0xfe, 0xb6, 0xc3, 0xe6, 0x7a, 0x97, 0x3f, 0x7d, 0xff, 0x99, 0xef, 0xd6, 0xe6, 0x5b, 0x8f, 0xe6, 0x8a, 0x36, 0x64, 0x23, 0xde, 0xc4, 0xc2, 0x4b, 0x42, 0x6, 0x8, 0x16, 0x26, 0xde, 0x82, 0x37, 0x31, 0xca, 0x40, 0x81, 0x60, 0xd2, 0xf0, 0xf2, 0xc3, 0x39, 0x7b, 0xa3, 0xf6, 0x7c, 0x68, 0xe8, 0xc4, 0x24, 0x80, 0xce, 0xcc, 0x5e, 0xbf, 0xe7, 0xfa, 0x57, 0x10, 0x70, 0xf5, 0xdd, 0x57, 0xc5, 0x33, 0xe8, 0x6, 0x1b, 0xf2, 0x3a, 0xdb, 0x4, 0x48, 0x1e, 0x5b, 0x5e, 0x67, 0xc8, 0x3a, 0xfd, 0xfe, 0xfc, 0xc5, 0xce, 0x96, 0xa9, 0x1a, 0x8c, 0x5e, 0x46, 0xb1, 0x8b, 0x82, 0xfa, 0xfb, 0xa7, 0x33, 0xd6, 0x5a, 0xfb, 0x6c, 0x5a, 0xef, 0x41, 0x7, 0xff, 0x6b, 0x46, 0x7a, 0x96, 0x54, 0x7a, 0xbd, 0xda, 0xf8, 0x48, 0xcf, 0x41, 0x60, 0x54, 0x47, 0xd9, 0x6, 0x49, 0xec, 0x6a, 0x80, 0x8c, 0x40, 0x32, 0xff, 0x93, 0xaa, 0xfb, 0x5f, 0x20, 0xcd, 0xd7, 0xa8, 0xfa, 0x84, 0x4c, 0xaa, 0x83, 0xa3, 0x6a, 0xf6, 0x36, 0xd0, 0x81, 0x8d, 0x5b, 0x7d, 0x84, 0xd5, 0x39, 0x0, 0xe, 0x53, 0xcb, 0xb1, 0x7c, 0x14, 0x9c, 0x8a, 0x7b, 0x3e, 0x74, 0xc6, 0x37, 0x2a, 0xf, 0x81, 0x96, 0xc4, 0x1c, 0xe3, 0x83, 0x2, 0x3e, 0xc7, 0xc4, 0x2d, 0x7, 0xc0, 0x14, 0x24, 0x58, 0x69, 0x9b, 0x98, 0x2b, 0x79, 0x3, 0x5c, 0x6, 0x4a, 0x30, 0x57, 0xda, 0x84, 0x31, 0xf6, 0x6d, 0xb4, 0x11, 0xf, 0xfa, 0xf7, 0x70, 0xf8, 0x4d, 0x94, 0xe0, 0x40, 0x19, 0x63, 0xa0, 0x49, 0x71, 0x78, 0xc5, 0xf9, 0x8f, 0xfe, 0x9b, 0x65, 0x46, 0xe2, 0x7d, 0x80, 0x57, 0x4a, 0xa, 0x24, 0x36, 0x16, 0x6c, 0x83, 0x88, 0xc2, 0xf8, 0x29, 0x2b, 0xc4, 0x5f, 0xc4, 0x2d, 0xb5, 0xa5, 0x1a, 0xe0, 0x81, 0x90, 0x5, 0xff, 0x61, 0xd, 0x42, 0x3c, 0x99, 0x16, 0x3c, 0xf0, 0xfd, 0xb3, 0x40, 0x44, 0x58, 0x56, 0x86, 0xe2, 0x58, 0xe1, 0xdb, 0x98, 0x80, 0x18, 0xb7, 0xfd, 0x5, 0x8b, 0xca, 0xca, 0x8, 0x84, 0x2c, 0x71, 0xac, 0x88, 0x8, 0xe0, 0x4, 0x10, 0x9, 0x1c, 0x94, 0xa5, 0x3, 0x22, 0x8b, 0x38, 0x9, 0xe3, 0xd6, 0x3b, 0x60, 0x0, 0x84, 0x0, 0xe9, 0x52, 0x7a, 0x17, 0x30, 0xe4, 0xd6, 0x16, 0xf4, 0x9, 0x80, 0x3b, 0x9a, 0x2c, 0xd6, 0xce, 0x56, 0x9e, 0xe8, 0x14, 0xf0, 0xe0, 0xed, 0x6, 0xe6, 0x44, 0xd, 0xb, 0x2d, 0x80, 0x1e, 0xee, 0x13, 0xbc, 0xe5, 0xb6, 0xa7, 0x8e, 0x20, 0x2d, 0x80, 0x3e, 0x8b, 0xfb, 0x1b, 0xbc, 0x23, 0x40, 0x4b, 0xf0, 0xaa, 0x57, 0xc0, 0x15, 0xad, 0xc3, 0xa0, 0xcd, 0xca, 0x3a, 0x80, 0x5e, 0x1, 0x70, 0xa8, 0x2c, 0x58, 0x7a, 0x56, 0xcf, 0xba, 0x5, 0x9c, 0x5b, 0xe4, 0xcb, 0x42, 0x6d, 0xca, 0x74, 0xb, 0xc8, 0x90, 0xbd, 0x16, 0x7d, 0x8, 0x17, 0xa0, 0x5b, 0x0, 0x2c, 0xe8, 0x93, 0xb8, 0x44, 0x36, 0x29, 0x8, 0xa0, 0x7, 0x6c, 0x9, 0xcd, 0xe4, 0x68, 0xb, 0xe9, 0x17, 0x90, 0xd0, 0xff, 0x33, 0x8f, 0xe8, 0xbe, 0x2a, 0x8, 0xa0, 0xaf, 0xf7, 0x11, 0x9a, 0x31, 0xcb, 0x31, 0x61, 0xfa, 0x5, 0xb0, 0x72, 0xc4, 0x4d, 0x68, 0x64, 0xc5, 0x51, 0x85, 0x8a, 0x0, 0xf2, 0x7d, 0x7c, 0x45, 0xf5, 0x68, 0xdd, 0x87, 0x80, 0x35, 0xe5, 0xf0, 0xa5, 0x2c, 0xd8, 0xf5, 0x21, 0x60, 0x57, 0xd6, 0x5f, 0xa0, 0x89, 0xa2, 0x2c, 0xc8, 0x95, 0x4, 0xd0, 0x21, 0x2b, 0xa8, 0x53, 0xf4, 0xec, 0x43, 0xc0, 0x93, 0x3a, 0x47, 0x31, 0xba, 0xc5, 0x4a, 0x2, 0xe8, 0x8b, 0x1c, 0xff, 0x4d, 0x1, 0xff, 0xda, 0x37, 0x83, 0x15, 0x4, 0xa1, 0x28, 0x88, 0x1a, 0x12, 0x6e, 0x14, 0x34, 0x5d, 0x24, 0x19, 0xd8, 0x3a, 0x41, 0xff, 0xff, 0xeb, 0xc2, 0xa0, 0x20, 0x31, 0x4f, 0x3a, 0xd2, 0x7d, 0xca, 0x9b, 0xb5, 0xbb, 0xd2, 0x7b, 0x67, 0xee, 0x19, 0xf3, 0x9f, 0xc0, 0xfc, 0x4f, 0x68, 0xfe, 0x1a, 0x9a, 0x7f, 0x88, 0xe0, 0x53, 0xc, 0xd6, 0x4c, 0xf8, 0x14, 0xf3, 0x30, 0x62, 0x73, 0x2a, 0xd, 0x23, 0x1e, 0xc7, 0x6c, 0xcf, 0x85, 0x71, 0xcc, 0xb, 0x9, 0x7, 0x14, 0x59, 0x20, 0x2c, 0x24, 0xb0, 0x92, 0x9, 0x11, 0xd, 0xac, 0x64, 0xca, 0x52, 0xca, 0x21, 0x15, 0x2f, 0xa5, 0xbc, 0x96, 0x4b, 0xe2, 0xb5, 0x9c, 0x8d, 0x89, 0x24, 0x36, 0x26, 0x6c, 0xcd, 0x34, 0xb1, 0x35, 0x63, 0x73, 0x2a, 0x89, 0xcd, 0x29, 0xdb, 0x73, 0x4d, 0x6c, 0xcf, 0x39, 0xa0, 0x60, 0xa5, 0x65, 0x9e, 0x97, 0x90, 0x81, 0x43, 0x40, 0x1, 0x11, 0xd, 0xc, 0x58, 0x18, 0xde, 0x10, 0xd1, 0x60, 0x48, 0x45, 0x3a, 0x43, 0xa0, 0x8, 0x21, 0x95, 0x1c, 0xd3, 0x85, 0xa7, 0x77, 0xa6, 0x1d, 0x2a, 0x31, 0x1d, 0x7, 0x95, 0xfc, 0x6c, 0x36, 0x27, 0xa8, 0x74, 0x3b, 0xaa, 0xd, 0xa, 0x83, 0xb0, 0x5a, 0x8f, 0xeb, 0x2f, 0x33, 0xe2, 0x7a, 0xf7, 0xf, 0x16, 0xe6, 0x27, 0x9b, 0x59, 0x47, 0xab, 0xb6, 0xdf, 0x7, 0xda, 0xe9, 0x47, 0x3e, 0x8f, 0x56, 0x5b, 0x38, 0xdb, 0xd9, 0x1f, 0x2e, 0xcd, 0x4f, 0xb7, 0xf6, 0xc7, 0xeb, 0xe1, 0xf9, 0x3e, 0x5a, 0xeb, 0x7c, 0xff, 0x67, 0x80, 0xa1, 0x1d, 0x2, 0xc, 0x5b, 0x42, 0x38, 0xec, 0x21, 0x16, 0x7b, 0x8c, 0x67, 0x4, 0x64, 0xaa, 0x7e, 0x7, 0x99, 0x2a, 0x0, 0x99, 0x96, 0xa2, 0x5c, 0x7, 0x15, 0xe5, 0xd2, 0x61, 0xb6, 0x26, 0x40, 0x35, 0xf7, 0x11, 0x98, 0x6d, 0x9b, 0x38, 0x9f, 0x3, 0x40, 0xa3, 0x3d, 0xd2, 0x89, 0x50, 0x6b, 0x17, 0xc7, 0xdd, 0x24, 0xd4, 0x7a, 0x3b, 0x6e, 0x1d, 0xeb, 0xb5, 0x7, 0x9b, 0x15, 0xb4, 0xfb, 0xba, 0x17, 0xb8, 0xdd, 0x1, 0xbc, 0xbf, 0x57, 0xb1, 0xb4, 0xe0, 0xb0, 0x9b, 0x8a, 0x87, 0x3, 0x25, 0x97, 0x57, 0xcd, 0xa7, 0xfe, 0x56, 0xf3, 0xa9, 0xa1, 0xe6, 0xb3, 0x66, 0xd1, 0x29, 0x1a, 0x16, 0x9d, 0x22, 0xa1, 0xe8, 0x24, 0x54, 0xbd, 0x92, 0x34, 0x4d, 0x9e, 0x55, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0xaf, 0xc5, 0x7a, 0x0, 0xb3, 0x43, 0xaa, 0xfb, 0xee, 0x9f, 0xf1, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon10Name[] = { 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x00 }; +const unsigned char defaultDialogIcon10Length[] = { 0x37, 0x32, 0x30, 0x00 }; +const unsigned char defaultDialogIcon10Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x40, 0x8, 0x3, 0x0, 0x0, 0x0, 0x9d, 0xb7, 0x81, 0xec, 0x0, 0x0, 0x0, 0x75, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x79, 0x59, 0x7d, 0x58, 0x0, 0x0, 0x0, 0x26, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x7, 0xd2, 0xab, 0x12, 0x22, 0x73, 0x50, 0x7d, 0x25, 0xcb, 0xa7, 0xb8, 0x7c, 0x60, 0x2e, 0xfa, 0x8f, 0xeb, 0xe3, 0xd9, 0xc3, 0x9e, 0x84, 0x69, 0x1d, 0xf1, 0xcd, 0xb2, 0x91, 0xb9, 0xed, 0x49, 0x48, 0x36, 0x35, 0x93, 0xf2, 0x7e, 0x13, 0xd0, 0xd1, 0x0, 0x0, 0x1, 0xe4, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xed, 0x57, 0xd9, 0x96, 0xc2, 0x20, 0xc, 0x2d, 0x4c, 0x37, 0xb5, 0x8b, 0xb5, 0xda, 0xd5, 0xba, 0xf, 0xff, 0xff, 0x89, 0x73, 0xa, 0x8c, 0xa9, 0x8, 0xd, 0x1e, 0x4e, 0xdf, 0xcc, 0x63, 0x48, 0xd2, 0xe6, 0xde, 0x24, 0x4, 0xef, 0x2b, 0x8b, 0x8a, 0x7f, 0x8b, 0x9a, 0xba, 0xaa, 0xea, 0x26, 0xba, 0xf9, 0x9f, 0x7b, 0x7, 0xe1, 0x81, 0x4d, 0xe4, 0x10, 0x6, 0x9f, 0x78, 0x93, 0x90, 0xb2, 0x37, 0xa1, 0x21, 0xb1, 0xf5, 0xcf, 0x4a, 0xa6, 0x95, 0x32, 0xb3, 0x72, 0xbf, 0x50, 0x66, 0x14, 0x7a, 0xc1, 0xfd, 0x87, 0xa9, 0xc3, 0x8e, 0x26, 0x9, 0xdd, 0x4d, 0x35, 0x5, 0x86, 0xfc, 0xe9, 0x69, 0xba, 0x4e, 0x73, 0x22, 0x21, 0xc9, 0xd3, 0xf5, 0x53, 0x7d, 0x9a, 0x65, 0x64, 0xb5, 0x97, 0x66, 0x5d, 0xda, 0xbe, 0x9e, 0xb4, 0x71, 0x27, 0x8f, 0xf6, 0xab, 0x19, 0xff, 0xb3, 0xb0, 0xd9, 0xe, 0x9a, 0xcf, 0xf8, 0xc3, 0x56, 0x9c, 0x9e, 0x8d, 0x11, 0x7c, 0xf9, 0xfd, 0x75, 0x60, 0x88, 0x2f, 0x13, 0xd9, 0x9b, 0xb2, 0x90, 0xf9, 0x6f, 0x8c, 0x7c, 0x93, 0x8d, 0xb0, 0x48, 0xf4, 0xc7, 0x85, 0x38, 0x4d, 0xe7, 0x40, 0x4a, 0x67, 0xb8, 0xb8, 0x20, 0xfe, 0x42, 0x62, 0x61, 0x75, 0xd5, 0x1c, 0x51, 0xf1, 0xff, 0x6a, 0x4f, 0xa8, 0x78, 0x88, 0x2c, 0xa8, 0xa6, 0x7e, 0x5, 0x7e, 0x4a, 0xfe, 0x47, 0xc6, 0x8e, 0xa, 0xe, 0x2, 0xc9, 0xec, 0xd, 0x9f, 0x92, 0xf3, 0xa7, 0x7c, 0x2f, 0x1a, 0x95, 0x91, 0xc2, 0x5, 0x67, 0xb3, 0x54, 0x91, 0xe, 0x79, 0xdc, 0x41, 0xfd, 0x5f, 0x5d, 0x56, 0xf, 0x6e, 0x1a, 0xea, 0x10, 0xe8, 0x54, 0x82, 0x7f, 0x46, 0xed, 0x8f, 0x5a, 0x2e, 0x9d, 0x6, 0x85, 0x40, 0x32, 0x80, 0x6, 0x0, 0x26, 0x2, 0x4d, 0x6, 0xad, 0x5d, 0x80, 0x56, 0x93, 0xc3, 0x81, 0x53, 0xe0, 0xd9, 0x5, 0xf0, 0x7a, 0x3e, 0xe5, 0x5e, 0xd2, 0x82, 0xc, 0xb0, 0x0, 0x90, 0xc3, 0x14, 0xb0, 0x1b, 0xd7, 0xe4, 0xb6, 0x1, 0x72, 0x6e, 0x7e, 0x57, 0xf9, 0x66, 0xc4, 0x36, 0x0, 0x61, 0x6a, 0x7d, 0x34, 0x7c, 0x7e, 0x79, 0x48, 0x0, 0x10, 0x3e, 0xe5, 0x9a, 0x89, 0xa2, 0x6, 0x66, 0xb1, 0x0, 0x50, 0x35, 0xf5, 0x44, 0x51, 0x41, 0x97, 0x5b, 0x5, 0x48, 0x46, 0x7d, 0xe5, 0x1c, 0xc0, 0x39, 0x5, 0xc, 0x44, 0x68, 0x26, 0x1c, 0x44, 0xa0, 0x11, 0x6f, 0x67, 0xa0, 0x11, 0x29, 0x24, 0x18, 0x28, 0x48, 0x21, 0x29, 0xa5, 0x8c, 0x8c, 0x34, 0x28, 0xe5, 0xad, 0x8f, 0x34, 0x13, 0x8, 0xde, 0x4c, 0xd0, 0xce, 0x6, 0xc1, 0xdb, 0x39, 0x30, 0xe4, 0x90, 0xf5, 0x7d, 0x66, 0x68, 0xc6, 0x0, 0x19, 0x69, 0x80, 0x56, 0x8e, 0x8f, 0x34, 0x18, 0xaa, 0xba, 0xcb, 0xaa, 0xc0, 0x87, 0x2a, 0x8c, 0xf5, 0x95, 0xfd, 0x58, 0xb7, 0xba, 0x58, 0xc8, 0x58, 0x7, 0x4, 0xb9, 0x58, 0x5c, 0xaf, 0x36, 0xf7, 0xcb, 0x55, 0xbd, 0xde, 0x63, 0xb, 0xff, 0xc2, 0x75, 0xc1, 0x58, 0x64, 0xc5, 0xc1, 0x97, 0xac, 0x7, 0x2c, 0x59, 0x16, 0x6b, 0x5e, 0xfc, 0xb6, 0xe6, 0xfd, 0xc2, 0x9a, 0x67, 0xb7, 0x68, 0xf6, 0x31, 0x2c, 0x9a, 0x71, 0xff, 0x54, 0x27, 0xd8, 0xea, 0x5f, 0x60, 0xab, 0x2e, 0x2a, 0xd7, 0xb9, 0x65, 0xfb, 0xea, 0xbe, 0xee, 0xbb, 0x3f, 0x38, 0x96, 0x7f, 0xf2, 0x0, 0x23, 0xf7, 0xff, 0x47, 0xd7, 0xdd, 0xf7, 0xbe, 0xb2, 0xa4, 0xfc, 0x1, 0xb3, 0x34, 0x7e, 0xa8, 0x9d, 0x48, 0x16, 0xe7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char defaultDialogIcon11Name[] = { 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x32, 0x78, 0x00 }; +const unsigned char defaultDialogIcon11Length[] = { 0x31, 0x32, 0x36, 0x32, 0x00 }; +const unsigned char defaultDialogIcon11Data[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x8, 0x3, 0x0, 0x0, 0x0, 0xf4, 0xe0, 0x91, 0xf9, 0x0, 0x0, 0x0, 0x93, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7a, 0x79, 0x23, 0x75, 0x0, 0x0, 0x0, 0x30, 0x74, 0x52, 0x4e, 0x53, 0x0, 0xfb, 0x55, 0x42, 0xc, 0xd7, 0xb7, 0x8a, 0x82, 0x7, 0x19, 0xe5, 0xc0, 0x48, 0x2b, 0x68, 0x62, 0x4e, 0xee, 0x94, 0x39, 0x6f, 0x21, 0xe8, 0xe2, 0xb4, 0x90, 0x33, 0xd0, 0xbd, 0x9, 0xcb, 0x97, 0x3c, 0xf, 0xc5, 0xb1, 0xf6, 0xa8, 0x7c, 0x52, 0x26, 0x25, 0x14, 0xc6, 0x61, 0x5d, 0x11, 0x8d, 0xf, 0xdf, 0x6c, 0x0, 0x0, 0x3, 0xda, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x97, 0xd9, 0x72, 0xb3, 0x30, 0xc, 0x85, 0xe5, 0x42, 0xc0, 0x6c, 0x9, 0x94, 0xa5, 0x49, 0xc9, 0xbe, 0xa7, 0x49, 0x3a, 0x7e, 0xff, 0xa7, 0xfb, 0xff, 0x8b, 0xce, 0xd8, 0x10, 0x62, 0xd9, 0xc6, 0x74, 0x7a, 0x91, 0xef, 0x5a, 0x4c, 0xe, 0xd2, 0x39, 0x52, 0x80, 0x17, 0x2f, 0x5e, 0xbc, 0x78, 0x61, 0xce, 0x32, 0xbc, 0xd4, 0x4e, 0x96, 0x39, 0xf5, 0x25, 0x5c, 0xc2, 0xef, 0xe2, 0x3a, 0x65, 0xbe, 0x88, 0x98, 0x40, 0xb4, 0xc8, 0x4b, 0xc7, 0x85, 0xdf, 0x20, 0x70, 0xe8, 0x8d, 0xb0, 0x4e, 0xc8, 0x8d, 0x3a, 0x1, 0xc, 0x4b, 0x72, 0x3c, 0x30, 0x29, 0x87, 0x63, 0x2, 0x83, 0x31, 0xf6, 0x3f, 0x98, 0x2, 0x1f, 0xfe, 0x18, 0x86, 0x60, 0xe3, 0x31, 0x65, 0xbc, 0x2, 0x6c, 0xe3, 0xc4, 0x4c, 0x8b, 0xd8, 0xb1, 0xdb, 0xfc, 0x98, 0x69, 0x13, 0xdb, 0x1b, 0x84, 0xeb, 0x13, 0x66, 0x0, 0xf1, 0x2d, 0xe5, 0x32, 0x9b, 0x32, 0x43, 0xa6, 0x19, 0x58, 0xc0, 0x67, 0x3d, 0xf0, 0xfb, 0xb7, 0x7f, 0xf6, 0xb4, 0xc3, 0xd1, 0x6a, 0xbf, 0x3e, 0xa5, 0xe9, 0x69, 0xbd, 0x5f, 0x45, 0x4f, 0x67, 0x34, 0xeb, 0x39, 0x86, 0x6a, 0xc4, 0xba, 0x98, 0xd3, 0x22, 0x84, 0x6, 0x61, 0x41, 0xe7, 0xac, 0x8b, 0x51, 0xd5, 0x2b, 0x7c, 0xef, 0x1d, 0x6f, 0x1e, 0x97, 0xd5, 0x13, 0xb5, 0x65, 0xdc, 0xd1, 0x89, 0xf7, 0x1e, 0x81, 0x9c, 0x10, 0xd6, 0xc6, 0xbb, 0x5f, 0x41, 0xc2, 0xf5, 0xee, 0x3d, 0x2a, 0x9e, 0x80, 0x19, 0x41, 0xce, 0xda, 0xec, 0xa, 0x40, 0xd9, 0xec, 0x58, 0x9b, 0x3c, 0x30, 0xfa, 0xfd, 0xd9, 0xc3, 0x38, 0xcf, 0xa0, 0xc4, 0x79, 0xf4, 0x60, 0x45, 0x13, 0x5, 0x79, 0xfb, 0xc6, 0x4c, 0x2, 0x65, 0xed, 0x93, 0x8f, 0x76, 0xf, 0xc, 0xe6, 0xdf, 0x1a, 0x24, 0x75, 0xb5, 0xd2, 0x4b, 0x5b, 0xf6, 0xd1, 0xf6, 0x81, 0x43, 0x10, 0x2b, 0x6b, 0x6, 0x88, 0x6c, 0x34, 0xf3, 0xdf, 0x7c, 0x7c, 0xf4, 0x5, 0xda, 0x7c, 0x8d, 0x9a, 0xaf, 0x50, 0x69, 0x75, 0xb0, 0xf9, 0xb0, 0xb7, 0x5, 0x3, 0xb6, 0x5e, 0xf3, 0x25, 0x74, 0x66, 0xd8, 0xc, 0xc0, 0x27, 0x18, 0xf2, 0xd9, 0x8c, 0x82, 0xe1, 0xfd, 0x21, 0x29, 0x18, 0x93, 0x12, 0xa3, 0xcb, 0x94, 0x31, 0x91, 0x14, 0x7a, 0x90, 0x32, 0x91, 0x4c, 0xd1, 0x0, 0x53, 0xa4, 0xff, 0xc6, 0x53, 0x98, 0x2e, 0xf5, 0x7, 0xe0, 0x41, 0x4f, 0x3c, 0xed, 0x21, 0x8c, 0x89, 0x68, 0xdd, 0xad, 0xdc, 0xe8, 0xdf, 0x79, 0xfe, 0x8d, 0x94, 0x88, 0x81, 0x22, 0x63, 0xc0, 0x89, 0xc5, 0xf0, 0xca, 0xf3, 0x9f, 0x44, 0xec, 0x3f, 0x51, 0x22, 0xdf, 0x7, 0xe2, 0x4a, 0x89, 0x1, 0xc5, 0x11, 0x5, 0x3b, 0x20, 0xa3, 0x26, 0x3f, 0x65, 0xb5, 0xfa, 0x52, 0x75, 0xb4, 0x1a, 0x40, 0x41, 0xca, 0x9c, 0xfd, 0xb0, 0x2, 0x29, 0x94, 0x71, 0x16, 0xe8, 0x35, 0x17, 0xef, 0x9f, 0xb, 0x32, 0x42, 0x5e, 0x19, 0xca, 0x63, 0x25, 0xde, 0xc6, 0x2, 0x33, 0xad, 0xfa, 0x5, 0x4b, 0x78, 0x65, 0xa2, 0x7e, 0x59, 0x3d, 0x24, 0x2, 0x62, 0x2, 0x90, 0xfb, 0xff, 0xc6, 0x4b, 0xdf, 0x40, 0x4a, 0x20, 0x26, 0x61, 0xac, 0xbc, 0x3, 0xce, 0x60, 0x4b, 0x0, 0x9c, 0x95, 0x77, 0x81, 0x30, 0xad, 0x1d, 0xd8, 0x13, 0x0, 0x37, 0x5e, 0x1b, 0x29, 0x8e, 0x95, 0x15, 0x36, 0x5, 0x6c, 0x98, 0x9a, 0x61, 0x8e, 0x98, 0x59, 0x70, 0x1, 0xb8, 0xb9, 0x8f, 0x12, 0xb3, 0x1c, 0x78, 0xd9, 0xdd, 0x50, 0x0, 0x7e, 0x16, 0xf, 0x81, 0xca, 0x16, 0x24, 0x57, 0xbb, 0x2, 0xae, 0x44, 0x65, 0x1b, 0x52, 0x7c, 0x69, 0xe3, 0x2, 0xf0, 0xd, 0x4b, 0x55, 0xbc, 0x5a, 0xda, 0x16, 0x50, 0x2a, 0xe4, 0xcb, 0x15, 0xda, 0x54, 0xd9, 0x16, 0x50, 0x9, 0xe3, 0x75, 0x71, 0xb, 0xcc, 0xc1, 0xb6, 0x0, 0x98, 0xb, 0x26, 0xc0, 0xbb, 0x44, 0xed, 0xb, 0xa0, 0xf8, 0x7c, 0x73, 0x61, 0xb, 0xd9, 0x17, 0x50, 0xe0, 0x5f, 0x8a, 0xb, 0xe1, 0xbe, 0xda, 0x17, 0x10, 0xe2, 0x7f, 0xa, 0x22, 0x6e, 0x13, 0xb0, 0x2f, 0x0, 0xb8, 0xc5, 0xa7, 0xd0, 0xc9, 0x12, 0x39, 0x18, 0x7d, 0x5, 0x44, 0xbc, 0x7e, 0x89, 0xf5, 0x68, 0x35, 0x84, 0x80, 0x15, 0x36, 0xe1, 0xb, 0x2f, 0xd8, 0xf, 0x21, 0x60, 0xcf, 0xeb, 0x2f, 0xd0, 0x45, 0xcd, 0xb, 0xd6, 0x43, 0x8, 0x58, 0xf3, 0xfa, 0x1a, 0xdb, 0x43, 0xa7, 0x21, 0x4, 0x9c, 0xb0, 0x4d, 0x94, 0xf1, 0x82, 0xb4, 0x97, 0x0, 0xfc, 0x22, 0x67, 0x7f, 0x53, 0xc0, 0xbf, 0xf6, 0xcd, 0x6e, 0x5, 0x41, 0x20, 0xa, 0xc2, 0x7, 0xea, 0x26, 0xa2, 0x5f, 0x22, 0x4b, 0x2a, 0x92, 0xba, 0x2c, 0xf4, 0xfd, 0x9f, 0x2e, 0xc, 0xa, 0x44, 0xf2, 0x6b, 0x99, 0x85, 0xe3, 0x8a, 0x73, 0x6d, 0x37, 0x95, 0xbb, 0x67, 0xbe, 0x33, 0xe3, 0xfe, 0x13, 0xb8, 0xff, 0x9, 0xdd, 0x5f, 0x43, 0xf7, 0x83, 0x8, 0x8e, 0x62, 0xb0, 0x66, 0xc2, 0x51, 0xcc, 0x97, 0x11, 0x9b, 0x53, 0xe9, 0x32, 0xe2, 0xeb, 0x98, 0xed, 0xb9, 0x70, 0x1d, 0xf3, 0x40, 0xc2, 0x80, 0x62, 0x69, 0xc2, 0x40, 0xd2, 0x31, 0x92, 0xa1, 0x16, 0x87, 0xf7, 0x17, 0xba, 0x30, 0x13, 0x46, 0x32, 0x1a, 0x4a, 0x1, 0x52, 0xe5, 0x39, 0x40, 0x2a, 0x18, 0x4a, 0x61, 0x2c, 0x17, 0x44, 0x63, 0x39, 0x1b, 0x13, 0x49, 0x6c, 0x4c, 0xd8, 0x9a, 0x69, 0x62, 0x6b, 0xc6, 0xe6, 0x54, 0x12, 0x9b, 0x53, 0xb6, 0xe7, 0x9a, 0xd8, 0x9e, 0x33, 0xa0, 0x60, 0x6d, 0x8e, 0x59, 0x76, 0x4, 0x6, 0xe, 0x80, 0x42, 0x40, 0x34, 0x66, 0x57, 0xb8, 0xbc, 0x1, 0xd1, 0xa8, 0x90, 0xca, 0x56, 0x0, 0x14, 0x1, 0x52, 0xc9, 0x98, 0xce, 0xb6, 0x5f, 0xa6, 0x6d, 0xa, 0xa6, 0x63, 0x50, 0xc9, 0xcf, 0x2e, 0x43, 0x40, 0x65, 0xbf, 0x51, 0xad, 0xed, 0x1c, 0x60, 0xb5, 0x8e, 0xeb, 0x4f, 0x1, 0xb8, 0xbe, 0xff, 0xb, 0xb, 0xf7, 0x95, 0x4d, 0xd0, 0xd2, 0xaa, 0xac, 0xe7, 0x81, 0xb2, 0xfb, 0x91, 0xe6, 0xd2, 0x2a, 0x85, 0xb5, 0x9d, 0xff, 0xe2, 0xd2, 0x7d, 0x75, 0xeb, 0xbf, 0xbc, 0xf6, 0x5f, 0xdf, 0xc7, 0x9, 0x30, 0x94, 0xad, 0x0, 0x83, 0x6b, 0x84, 0x23, 0xdb, 0xa7, 0x15, 0x62, 0xf1, 0x8f, 0xf1, 0xf8, 0x7, 0x99, 0xe2, 0x46, 0xb9, 0xa2, 0x85, 0xd9, 0xa, 0x43, 0x15, 0x8f, 0xd6, 0xc7, 0xf2, 0x59, 0xc4, 0x38, 0xdf, 0xba, 0x3b, 0xce, 0xb7, 0x86, 0x38, 0x9f, 0x47, 0xa0, 0xb1, 0x48, 0x39, 0xd2, 0xc9, 0xa1, 0xd6, 0xe7, 0xb3, 0x3b, 0xd4, 0x7a, 0x9f, 0xa6, 0x1e, 0xeb, 0xed, 0x41, 0xb0, 0x59, 0x88, 0x76, 0x5f, 0x86, 0x12, 0x6e, 0xaf, 0x35, 0x39, 0x57, 0x41, 0x3a, 0x4f, 0x2c, 0xb6, 0x76, 0x7a, 0xc1, 0x41, 0xaf, 0x78, 0xcc, 0xab, 0x3f, 0x34, 0x6f, 0x57, 0x3c, 0x86, 0x52, 0x72, 0xf9, 0xd4, 0x7c, 0x6e, 0xbf, 0x6a, 0x3e, 0x37, 0xa8, 0xf9, 0xc4, 0x2d, 0x3a, 0x1d, 0x1a, 0x6f, 0x7c, 0x50, 0xd1, 0x29, 0xfd, 0xaa, 0xd7, 0xa8, 0x51, 0xa3, 0x46, 0xd, 0x4a, 0x2f, 0xb9, 0x87, 0x9d, 0xa4, 0x64, 0xb5, 0x51, 0x8a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x00 }; +const unsigned char *defaultDialogIcons[] = { defaultDialogIcon0Name, defaultDialogIcon0Length, defaultDialogIcon0Data, defaultDialogIcon1Name, defaultDialogIcon1Length, defaultDialogIcon1Data, defaultDialogIcon2Name, defaultDialogIcon2Length, defaultDialogIcon2Data, defaultDialogIcon3Name, defaultDialogIcon3Length, defaultDialogIcon3Data, defaultDialogIcon4Name, defaultDialogIcon4Length, defaultDialogIcon4Data, defaultDialogIcon5Name, defaultDialogIcon5Length, defaultDialogIcon5Data, defaultDialogIcon6Name, defaultDialogIcon6Length, defaultDialogIcon6Data, defaultDialogIcon7Name, defaultDialogIcon7Length, defaultDialogIcon7Data, defaultDialogIcon8Name, defaultDialogIcon8Length, defaultDialogIcon8Data, defaultDialogIcon9Name, defaultDialogIcon9Length, defaultDialogIcon9Data, defaultDialogIcon10Name, defaultDialogIcon10Length, defaultDialogIcon10Data, defaultDialogIcon11Name, defaultDialogIcon11Length, defaultDialogIcon11Data, 0x00 }; diff --git a/v2/internal/ffenestri/effectstructs_windows.h b/v2/internal/ffenestri/effectstructs_windows.h new file mode 100644 index 000000000..8313c4538 --- /dev/null +++ b/v2/internal/ffenestri/effectstructs_windows.h @@ -0,0 +1,64 @@ +// Credit: https://gist.github.com/ysc3839/b08d2bff1c7dacde529bed1d37e85ccf +#pragma once + +typedef enum _WINDOWCOMPOSITIONATTRIB +{ + WCA_UNDEFINED = 0, + WCA_NCRENDERING_ENABLED = 1, + WCA_NCRENDERING_POLICY = 2, + WCA_TRANSITIONS_FORCEDISABLED = 3, + WCA_ALLOW_NCPAINT = 4, + WCA_CAPTION_BUTTON_BOUNDS = 5, + WCA_NONCLIENT_RTL_LAYOUT = 6, + WCA_FORCE_ICONIC_REPRESENTATION = 7, + WCA_EXTENDED_FRAME_BOUNDS = 8, + WCA_HAS_ICONIC_BITMAP = 9, + WCA_THEME_ATTRIBUTES = 10, + WCA_NCRENDERING_EXILED = 11, + WCA_NCADORNMENTINFO = 12, + WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, + WCA_VIDEO_OVERLAY_ACTIVE = 14, + WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, + WCA_DISALLOW_PEEK = 16, + WCA_CLOAK = 17, + WCA_CLOAKED = 18, + WCA_ACCENT_POLICY = 19, + WCA_FREEZE_REPRESENTATION = 20, + WCA_EVER_UNCLOAKED = 21, + WCA_VISUAL_OWNER = 22, + WCA_HOLOGRAPHIC = 23, + WCA_EXCLUDED_FROM_DDA = 24, + WCA_PASSIVEUPDATEMODE = 25, + WCA_USEDARKMODECOLORS = 26, + WCA_LAST = 27 +} WINDOWCOMPOSITIONATTRIB; + +typedef struct _WINDOWCOMPOSITIONATTRIBDATA +{ + WINDOWCOMPOSITIONATTRIB Attrib; + PVOID pvData; + SIZE_T cbData; +} WINDOWCOMPOSITIONATTRIBDATA; + +typedef enum _ACCENT_STATE +{ + ACCENT_DISABLED = 0, + ACCENT_ENABLE_GRADIENT = 1, + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + ACCENT_ENABLE_BLURBEHIND = 3, + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803 + ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809 + ACCENT_INVALID_STATE = 6 +} ACCENT_STATE; + +typedef struct _ACCENT_POLICY +{ + ACCENT_STATE AccentState; + DWORD AccentFlags; + DWORD GradientColor; + DWORD AnimationId; +} ACCENT_POLICY; + +typedef BOOL (WINAPI *pfnGetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*); + +typedef BOOL (WINAPI *pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*); \ No newline at end of file diff --git a/v2/internal/ffenestri/ffenestri.go b/v2/internal/ffenestri/ffenestri.go new file mode 100644 index 000000000..15205b727 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri.go @@ -0,0 +1,178 @@ +package ffenestri + +import ( + "runtime" + "strings" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/menumanager" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher" + "github.com/wailsapp/wails/v2/pkg/options" +) + +/* +#cgo linux CFLAGS: -DFFENESTRI_LINUX=1 +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 + +#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1 +#cgo darwin LDFLAGS: -framework WebKit -lobjc + +#cgo windows CXXFLAGS: -std=c++11 +#cgo windows,amd64 LDFLAGS: -L./windows/x64 -lWebView2Loader -lgdi32 -lole32 -lShlwapi -luser32 -loleaut32 -ldwmapi + +#include +#include "ffenestri.h" +*/ +import "C" + +// Application is our main application object +type Application struct { + config *options.App + memory []unsafe.Pointer + + // This is the main app pointer + app *C.struct_Application + + // Manages menus + menuManager *menumanager.Manager + + // Logger + logger logger.CustomLogger +} + +func (a *Application) saveMemoryReference(mem unsafe.Pointer) { + a.memory = append(a.memory, mem) +} + +func (a *Application) string2CString(str string) *C.char { + result := C.CString(str) + a.saveMemoryReference(unsafe.Pointer(result)) + return result +} + +func init() { + runtime.LockOSThread() +} + +// NewApplicationWithConfig creates a new application based on the given config +func NewApplicationWithConfig(config *options.App, logger *logger.Logger, menuManager *menumanager.Manager) *Application { + return &Application{ + config: config, + logger: logger.CustomLogger("Ffenestri"), + menuManager: menuManager, + } +} + +func (a *Application) freeMemory() { + for _, mem := range a.memory { + // fmt.Printf("Freeing memory: %+v\n", mem) + C.free(mem) + } +} + +// bool2Cint converts a Go boolean to a C integer +func (a *Application) bool2Cint(value bool) C.int { + if value { + return C.int(1) + } + return C.int(0) +} + +// dispatcher is the interface to send messages to +var dispatcher *messagedispatcher.DispatchClient + +// Dispatcher is what we register out client with +type Dispatcher interface { + RegisterClient(client messagedispatcher.Client) *messagedispatcher.DispatchClient +} + +// DispatchClient is the means for passing messages to the backend +type DispatchClient interface { + SendMessage(string) +} + +func intToColour(colour int) (C.int, C.int, C.int, C.int) { + var alpha = C.int(colour & 0xFF) + var blue = C.int((colour >> 8) & 0xFF) + var green = C.int((colour >> 16) & 0xFF) + var red = C.int((colour >> 24) & 0xFF) + return red, green, blue, alpha +} + +// Run the application +func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug bool) error { + title := a.string2CString(a.config.Title) + width := C.int(a.config.Width) + height := C.int(a.config.Height) + resizable := a.bool2Cint(!a.config.DisableResize) + devtools := a.bool2Cint(a.config.DevTools) + fullscreen := a.bool2Cint(a.config.Fullscreen) + startHidden := a.bool2Cint(a.config.StartHidden) + logLevel := C.int(a.config.LogLevel) + hideWindowOnClose := a.bool2Cint(a.config.HideWindowOnClose) + app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel, hideWindowOnClose) + + // Save app reference + a.app = (*C.struct_Application)(app) + + // Set Min Window Size + minWidth := C.int(a.config.MinWidth) + minHeight := C.int(a.config.MinHeight) + C.SetMinWindowSize(a.app, minWidth, minHeight) + + // Set Max Window Size + maxWidth := C.int(a.config.MaxWidth) + maxHeight := C.int(a.config.MaxHeight) + C.SetMaxWindowSize(a.app, maxWidth, maxHeight) + + // Set debug if needed + C.SetDebug(app, a.bool2Cint(debug)) + + if a.config.Frameless { + C.DisableFrame(a.app) + } + + if a.config.RGBA != 0 { + r, g, b, alpha := intToColour(a.config.RGBA) + C.SetColour(a.app, r, g, b, alpha) + } + + // Escape bindings so C doesn't freak out + bindings = strings.ReplaceAll(bindings, `"`, `\"`) + + // Set bindings + C.SetBindings(app, a.string2CString(bindings)) + + // save the dispatcher in a package variable so that the C callbacks + // can access it + dispatcher = incomingDispatcher.RegisterClient(newClient(a)) + + // Process platform settings + err := a.processPlatformSettings() + if err != nil { + return err + } + + // Check we could initialise the application + if app != nil { + // Yes - Save memory reference and run app, cleaning up afterwards + a.saveMemoryReference(unsafe.Pointer(app)) + C.Run(app, 0, nil) + } else { + // Oh no! We couldn't initialise the application + a.logger.Fatal("Cannot initialise Application.") + } + //println("\n\n\n\n\n\nhererererer\n\n\n\n") + a.freeMemory() + return nil +} + +// messageFromWindowCallback is called by any messages sent in +// webkit to window.external.invoke. It relays the message on to +// the dispatcher. +//export messageFromWindowCallback +func messageFromWindowCallback(data *C.char) { + dispatcher.DispatchMessage(C.GoString(data)) +} diff --git a/v2/internal/ffenestri/ffenestri.h b/v2/internal/ffenestri/ffenestri.h new file mode 100644 index 000000000..1bbc9d907 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri.h @@ -0,0 +1,56 @@ +#ifndef __FFENESTRI_H__ +#define __FFENESTRI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +struct Application; + +extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose); +extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight); +extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight); +extern void Run(struct Application*, int argc, char **argv); +extern void DestroyApplication(struct Application*); +extern void SetDebug(struct Application*, int flag); +extern void SetBindings(struct Application*, const char *bindings); +extern void ExecJS(struct Application*, const char *script); +extern void Hide(struct Application*); +extern void Show(struct Application*); +extern void Center(struct Application*); +extern void Maximise(struct Application*); +extern void Unmaximise(struct Application*); +extern void ToggleMaximise(struct Application*); +extern void Minimise(struct Application*); +extern void Unminimise(struct Application*); +extern void ToggleMinimise(struct Application*); +extern void SetColour(struct Application*, int red, int green, int blue, int alpha); +extern void SetSize(struct Application*, int width, int height); +extern void SetPosition(struct Application*, int x, int y); +extern void Quit(struct Application*); +extern void SetTitle(struct Application*, const char *title); +extern void Fullscreen(struct Application*); +extern void UnFullscreen(struct Application*); +extern void ToggleFullscreen(struct Application*); +extern void DisableFrame(struct Application*); +extern void OpenDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories); +extern void SaveDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories); +extern void MessageDialog(struct Application*, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton); +extern void DarkModeEnabled(struct Application*, char *callbackID); +extern void SetApplicationMenu(struct Application*, const char *); +extern void AddTrayMenu(struct Application*, const char *menuTrayJSON); +extern void SetTrayMenu(struct Application*, const char *menuTrayJSON); +extern void DeleteTrayMenuByID(struct Application*, const char *id); +extern void UpdateTrayMenuLabel(struct Application*, const char* JSON); +extern void AddContextMenu(struct Application*, char *contextMenuJSON); +extern void UpdateContextMenu(struct Application*, char *contextMenuJSON); +extern void WebviewIsTransparent(struct Application*); +extern void WindowIsTranslucent(struct Application*); +extern void* GetWindowHandle(struct Application*); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/v2/internal/ffenestri/ffenestri_client.go b/v2/internal/ffenestri/ffenestri_client.go new file mode 100644 index 000000000..05af2cdf2 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_client.go @@ -0,0 +1,272 @@ +// +build !windows + +package ffenestri + +/* +#include "ffenestri.h" +*/ +import "C" + +import ( + goruntime "runtime" + "strconv" + "strings" + + "github.com/wailsapp/wails/v2/pkg/runtime" + + "github.com/wailsapp/wails/v2/internal/logger" +) + +// Client is our implementation of messageDispatcher.Client +type Client struct { + app *Application + logger logger.CustomLogger +} + +func newClient(app *Application) *Client { + return &Client{ + app: app, + logger: app.logger, + } +} + +// Quit the application +func (c *Client) Quit() { + c.app.logger.Trace("Got shutdown message") + C.Quit(c.app.app) +} + +// NotifyEvent will pass on the event message to the frontend +func (c *Client) NotifyEvent(message string) { + eventMessage := `window.wails._.Notify(` + strconv.Quote(message) + `);` + c.app.logger.Trace("eventMessage = %+v", eventMessage) + C.ExecJS(c.app.app, c.app.string2CString(eventMessage)) +} + +// CallResult contains the result of the call from JS +func (c *Client) CallResult(message string) { + callbackMessage := `window.wails._.Callback(` + strconv.Quote(message) + `);` + c.app.logger.Trace("callbackMessage = %+v", callbackMessage) + C.ExecJS(c.app.app, c.app.string2CString(callbackMessage)) +} + +// WindowSetTitle sets the window title to the given string +func (c *Client) WindowSetTitle(title string) { + C.SetTitle(c.app.app, c.app.string2CString(title)) +} + +// WindowFullscreen will set the window to be fullscreen +func (c *Client) WindowFullscreen() { + C.Fullscreen(c.app.app) +} + +// WindowUnFullscreen will unfullscreen the window +func (c *Client) WindowUnFullscreen() { + C.UnFullscreen(c.app.app) +} + +// WindowShow will show the window +func (c *Client) WindowShow() { + C.Show(c.app.app) +} + +// WindowHide will hide the window +func (c *Client) WindowHide() { + C.Hide(c.app.app) +} + +// WindowCenter will hide the window +func (c *Client) WindowCenter() { + C.Center(c.app.app) +} + +// WindowMaximise will maximise the window +func (c *Client) WindowMaximise() { + C.Maximise(c.app.app) +} + +// WindowMinimise will minimise the window +func (c *Client) WindowMinimise() { + C.Minimise(c.app.app) +} + +// WindowUnmaximise will unmaximise the window +func (c *Client) WindowUnmaximise() { + C.Unmaximise(c.app.app) +} + +// WindowUnminimise will unminimise the window +func (c *Client) WindowUnminimise() { + C.Unminimise(c.app.app) +} + +// WindowPosition will position the window to x,y on the +// monitor that the window is mostly on +func (c *Client) WindowPosition(x int, y int) { + C.SetPosition(c.app.app, C.int(x), C.int(y)) +} + +// WindowSize will resize the window to the given +// width and height +func (c *Client) WindowSize(width int, height int) { + C.SetSize(c.app.app, C.int(width), C.int(height)) +} + +func (c *Client) WindowSetMinSize(width int, height int) { + C.SetMinWindowSize(c.app.app, C.int(width), C.int(height)) +} + +func (c *Client) WindowSetMaxSize(width int, height int) { + C.SetMaxWindowSize(c.app.app, C.int(width), C.int(height)) +} + +// WindowSetColour sets the window colour +func (c *Client) WindowSetColour(colour int) { + r, g, b, a := intToColour(colour) + C.SetColour(c.app.app, r, g, b, a) +} + +// OpenFileDialog will open a dialog with the given title and filter +func (c *Client) OpenFileDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + filters := []string{} + if goruntime.GOOS == "darwin" { + for _, filter := range dialogOptions.Filters { + filters = append(filters, strings.Split(filter.Pattern, ",")...) + } + } + C.OpenDialog(c.app.app, + c.app.string2CString(callbackID), + c.app.string2CString(dialogOptions.Title), + c.app.string2CString(strings.Join(filters, ";")), + c.app.string2CString(dialogOptions.DefaultFilename), + c.app.string2CString(dialogOptions.DefaultDirectory), + c.app.bool2Cint(dialogOptions.AllowFiles), + c.app.bool2Cint(dialogOptions.AllowDirectories), + c.app.bool2Cint(false), + c.app.bool2Cint(dialogOptions.ShowHiddenFiles), + c.app.bool2Cint(dialogOptions.CanCreateDirectories), + c.app.bool2Cint(dialogOptions.ResolvesAliases), + c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories), + ) +} + +// OpenDirectoryDialog will open a dialog with the given title and filter +func (c *Client) OpenDirectoryDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + filters := []string{} + if goruntime.GOOS == "darwin" { + for _, filter := range dialogOptions.Filters { + filters = append(filters, strings.Split(filter.Pattern, ",")...) + } + } + C.OpenDialog(c.app.app, + c.app.string2CString(callbackID), + c.app.string2CString(dialogOptions.Title), + c.app.string2CString(strings.Join(filters, ";")), + c.app.string2CString(dialogOptions.DefaultFilename), + c.app.string2CString(dialogOptions.DefaultDirectory), + c.app.bool2Cint(false), // Files + c.app.bool2Cint(true), // Directories + c.app.bool2Cint(false), // Multiple + c.app.bool2Cint(dialogOptions.ShowHiddenFiles), + c.app.bool2Cint(dialogOptions.CanCreateDirectories), + c.app.bool2Cint(dialogOptions.ResolvesAliases), + c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories), + ) +} + +// OpenMultipleFilesDialog will open a dialog with the given title and filter +func (c *Client) OpenMultipleFilesDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + filters := []string{} + if goruntime.GOOS == "darwin" { + for _, filter := range dialogOptions.Filters { + filters = append(filters, strings.Split(filter.Pattern, ",")...) + } + } + C.OpenDialog(c.app.app, + c.app.string2CString(callbackID), + c.app.string2CString(dialogOptions.Title), + c.app.string2CString(strings.Join(filters, ";")), + c.app.string2CString(dialogOptions.DefaultFilename), + c.app.string2CString(dialogOptions.DefaultDirectory), + c.app.bool2Cint(dialogOptions.AllowFiles), + c.app.bool2Cint(dialogOptions.AllowDirectories), + c.app.bool2Cint(true), + c.app.bool2Cint(dialogOptions.ShowHiddenFiles), + c.app.bool2Cint(dialogOptions.CanCreateDirectories), + c.app.bool2Cint(dialogOptions.ResolvesAliases), + c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories), + ) +} + +// SaveDialog will open a dialog with the given title and filter +func (c *Client) SaveDialog(dialogOptions *runtime.SaveDialogOptions, callbackID string) { + filters := []string{} + if goruntime.GOOS == "darwin" { + for _, filter := range dialogOptions.Filters { + filters = append(filters, strings.Split(filter.Pattern, ",")...) + } + } + C.SaveDialog(c.app.app, + c.app.string2CString(callbackID), + c.app.string2CString(dialogOptions.Title), + c.app.string2CString(strings.Join(filters, ";")), + c.app.string2CString(dialogOptions.DefaultFilename), + c.app.string2CString(dialogOptions.DefaultDirectory), + c.app.bool2Cint(dialogOptions.ShowHiddenFiles), + c.app.bool2Cint(dialogOptions.CanCreateDirectories), + c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories), + ) +} + +// MessageDialog will open a message dialog with the given options +func (c *Client) MessageDialog(dialogOptions runtime.MessageDialogOptions, callbackID string) { + + // Sanity check button length + if len(dialogOptions.Buttons) > 4 { + c.app.logger.Error("Given %d message dialog buttons. Maximum is 4", len(dialogOptions.Buttons)) + return + } + + // Process buttons + buttons := []string{"", "", "", ""} + for i, button := range dialogOptions.Buttons { + buttons[i] = button + } + + C.MessageDialog(c.app.app, + c.app.string2CString(callbackID), + c.app.string2CString(string(dialogOptions.Type)), + c.app.string2CString(dialogOptions.Title), + c.app.string2CString(dialogOptions.Message), + c.app.string2CString(dialogOptions.Icon), + c.app.string2CString(buttons[0]), + c.app.string2CString(buttons[1]), + c.app.string2CString(buttons[2]), + c.app.string2CString(buttons[3]), + c.app.string2CString(dialogOptions.DefaultButton), + c.app.string2CString(dialogOptions.CancelButton)) +} + +func (c *Client) DarkModeEnabled(callbackID string) { + C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID)) +} + +func (c *Client) SetApplicationMenu(applicationMenuJSON string) { + C.SetApplicationMenu(c.app.app, c.app.string2CString(applicationMenuJSON)) +} + +func (c *Client) SetTrayMenu(trayMenuJSON string) { + C.SetTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON)) +} + +func (c *Client) UpdateTrayMenuLabel(JSON string) { + C.UpdateTrayMenuLabel(c.app.app, c.app.string2CString(JSON)) +} + +func (c *Client) UpdateContextMenu(contextMenuJSON string) { + C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON)) +} + +func (c *Client) DeleteTrayMenuByID(id string) { + C.DeleteTrayMenuByID(c.app.app, c.app.string2CString(id)) +} diff --git a/v2/internal/ffenestri/ffenestri_client_windows.go b/v2/internal/ffenestri/ffenestri_client_windows.go new file mode 100644 index 000000000..542fbf79a --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_client_windows.go @@ -0,0 +1,308 @@ +// +build windows + +package ffenestri + +/* +#include "ffenestri.h" +*/ +import "C" + +import ( + "encoding/json" + "github.com/leaanthony/go-common-file-dialog/cfd" + "github.com/wailsapp/wails/v2/pkg/runtime" + "golang.org/x/sys/windows" + "log" + "strconv" + "syscall" + + "github.com/wailsapp/wails/v2/internal/logger" +) + +// Client is our implementation of messageDispatcher.Client +type Client struct { + app *Application + logger logger.CustomLogger +} + +func newClient(app *Application) *Client { + return &Client{ + app: app, + logger: app.logger, + } +} + +// Quit the application +func (c *Client) Quit() { + c.app.logger.Trace("Got shutdown message") + C.Quit(c.app.app) +} + +// NotifyEvent will pass on the event message to the frontend +func (c *Client) NotifyEvent(message string) { + eventMessage := `window.wails._.Notify(` + strconv.Quote(message) + `);` + c.app.logger.Trace("eventMessage = %+v", eventMessage) + C.ExecJS(c.app.app, c.app.string2CString(eventMessage)) +} + +// CallResult contains the result of the call from JS +func (c *Client) CallResult(message string) { + callbackMessage := `window.wails._.Callback(` + strconv.Quote(message) + `);` + c.app.logger.Trace("callbackMessage = %+v", callbackMessage) + C.ExecJS(c.app.app, c.app.string2CString(callbackMessage)) +} + +// WindowSetTitle sets the window title to the given string +func (c *Client) WindowSetTitle(title string) { + C.SetTitle(c.app.app, c.app.string2CString(title)) +} + +// WindowFullscreen will set the window to be fullscreen +func (c *Client) WindowFullscreen() { + C.Fullscreen(c.app.app) +} + +// WindowUnFullscreen will unfullscreen the window +func (c *Client) WindowUnFullscreen() { + C.UnFullscreen(c.app.app) +} + +// WindowShow will show the window +func (c *Client) WindowShow() { + C.Show(c.app.app) +} + +// WindowHide will hide the window +func (c *Client) WindowHide() { + C.Hide(c.app.app) +} + +// WindowCenter will hide the window +func (c *Client) WindowCenter() { + C.Center(c.app.app) +} + +// WindowMaximise will maximise the window +func (c *Client) WindowMaximise() { + C.Maximise(c.app.app) +} + +// WindowMinimise will minimise the window +func (c *Client) WindowMinimise() { + C.Minimise(c.app.app) +} + +// WindowUnmaximise will unmaximise the window +func (c *Client) WindowUnmaximise() { + C.Unmaximise(c.app.app) +} + +// WindowUnminimise will unminimise the window +func (c *Client) WindowUnminimise() { + C.Unminimise(c.app.app) +} + +// WindowPosition will position the window to x,y on the +// monitor that the window is mostly on +func (c *Client) WindowPosition(x int, y int) { + C.SetPosition(c.app.app, C.int(x), C.int(y)) +} + +// WindowSize will resize the window to the given +// width and height +func (c *Client) WindowSize(width int, height int) { + C.SetSize(c.app.app, C.int(width), C.int(height)) +} + +// WindowSetMinSize sets the minimum window size +func (c *Client) WindowSetMinSize(width int, height int) { + C.SetMinWindowSize(c.app.app, C.int(width), C.int(height)) +} + +// WindowSetMaxSize sets the maximum window size +func (c *Client) WindowSetMaxSize(width int, height int) { + C.SetMaxWindowSize(c.app.app, C.int(width), C.int(height)) +} + +// WindowSetColour sets the window colour +func (c *Client) WindowSetColour(colour int) { + r, g, b, a := intToColour(colour) + C.SetColour(c.app.app, r, g, b, a) +} + +func convertFilters(filters []runtime.FileFilter) []cfd.FileFilter { + var result []cfd.FileFilter + for _, filter := range filters { + result = append(result, cfd.FileFilter(filter)) + } + return result +} + +// OpenFileDialog will open a dialog with the given title and filter +func (c *Client) OpenFileDialog(options runtime.OpenDialogOptions, callbackID string) { + config := cfd.DialogConfig{ + Folder: options.DefaultDirectory, + FileFilters: convertFilters(options.Filters), + FileName: options.DefaultFilename, + } + thisdialog, err := cfd.NewOpenFileDialog(config) + if err != nil { + log.Fatal(err) + } + thisdialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app))) + defer func(thisdialog cfd.OpenFileDialog) { + err := thisdialog.Release() + if err != nil { + log.Fatal(err) + } + }(thisdialog) + result, err := thisdialog.ShowAndGetResult() + if err != nil && err != cfd.ErrorCancelled { + log.Fatal(err) + } + + dispatcher.DispatchMessage("DO" + callbackID + "|" + result) +} + +// OpenDirectoryDialog will open a dialog with the given title and filter +func (c *Client) OpenDirectoryDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + config := cfd.DialogConfig{ + Title: dialogOptions.Title, + Role: "PickFolder", + Folder: dialogOptions.DefaultDirectory, + } + thisDialog, err := cfd.NewSelectFolderDialog(config) + if err != nil { + log.Fatal() + } + thisDialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app))) + defer func(thisDialog cfd.SelectFolderDialog) { + err := thisDialog.Release() + if err != nil { + log.Fatal(err) + } + }(thisDialog) + result, err := thisDialog.ShowAndGetResult() + if err != nil && err != cfd.ErrorCancelled { + log.Fatal(err) + } + dispatcher.DispatchMessage("DD" + callbackID + "|" + result) +} + +// OpenMultipleFilesDialog will open a dialog with the given title and filter +func (c *Client) OpenMultipleFilesDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + config := cfd.DialogConfig{ + Title: dialogOptions.Title, + Role: "OpenMultipleFiles", + FileFilters: convertFilters(dialogOptions.Filters), + FileName: dialogOptions.DefaultFilename, + Folder: dialogOptions.DefaultDirectory, + } + thisdialog, err := cfd.NewOpenMultipleFilesDialog(config) + if err != nil { + log.Fatal(err) + } + thisdialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app))) + defer func(thisdialog cfd.OpenMultipleFilesDialog) { + err := thisdialog.Release() + if err != nil { + log.Fatal(err) + } + }(thisdialog) + result, err := thisdialog.ShowAndGetResults() + if err != nil && err != cfd.ErrorCancelled { + log.Fatal(err) + } + resultJSON, err := json.Marshal(result) + if err != nil { + log.Fatal(err) + } + dispatcher.DispatchMessage("D*" + callbackID + "|" + string(resultJSON)) +} + +// SaveDialog will open a dialog with the given title and filter +func (c *Client) SaveDialog(dialogOptions runtime.SaveDialogOptions, callbackID string) { + saveDialog, err := cfd.NewSaveFileDialog(cfd.DialogConfig{ + Title: dialogOptions.Title, + Role: "SaveFile", + FileFilters: convertFilters(dialogOptions.Filters), + FileName: dialogOptions.DefaultFilename, + Folder: dialogOptions.DefaultDirectory, + }) + if err != nil { + log.Fatal(err) + } + saveDialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app))) + err = saveDialog.Show() + if err != nil { + log.Fatal(err) + } + result, err := saveDialog.GetResult() + if err != nil && err != cfd.ErrorCancelled { + log.Fatal(err) + } + dispatcher.DispatchMessage("DS" + callbackID + "|" + result) +} + +// MessageDialog will open a message dialog with the given options +func (c *Client) MessageDialog(options runtime.MessageDialogOptions, callbackID string) { + + title, err := syscall.UTF16PtrFromString(options.Title) + if err != nil { + log.Fatal(err) + } + message, err := syscall.UTF16PtrFromString(options.Message) + if err != nil { + log.Fatal(err) + } + var flags uint32 + switch options.Type { + case runtime.InfoDialog: + flags = windows.MB_OK | windows.MB_ICONINFORMATION + case runtime.ErrorDialog: + flags = windows.MB_ICONERROR | windows.MB_OK + case runtime.QuestionDialog: + flags = windows.MB_YESNO + case runtime.WarningDialog: + flags = windows.MB_OK | windows.MB_ICONWARNING + } + + button, _ := windows.MessageBox(windows.HWND(C.GetWindowHandle(c.app.app)), message, title, flags|windows.MB_SYSTEMMODAL) + // This maps MessageBox return values to strings + responses := []string{"", "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "", "", "Try Again", "Continue"} + result := "Error" + if int(button) < len(responses) { + result = responses[button] + } + dispatcher.DispatchMessage("DM" + callbackID + "|" + result) +} + +// DarkModeEnabled sets the application to use dark mode +func (c *Client) DarkModeEnabled(callbackID string) { + C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID)) +} + +// SetApplicationMenu sets the application menu +func (c *Client) SetApplicationMenu(_ string) { + c.updateApplicationMenu() +} + +// SetTrayMenu sets the tray menu +func (c *Client) SetTrayMenu(trayMenuJSON string) { + C.SetTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON)) +} + +// UpdateTrayMenuLabel updates a tray menu label +func (c *Client) UpdateTrayMenuLabel(JSON string) { + C.UpdateTrayMenuLabel(c.app.app, c.app.string2CString(JSON)) +} + +// UpdateContextMenu will update the current context menu +func (c *Client) UpdateContextMenu(contextMenuJSON string) { + C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON)) +} + +// DeleteTrayMenuByID will remove a tray menu based on the given id +func (c *Client) DeleteTrayMenuByID(id string) { + C.DeleteTrayMenuByID(c.app.app, c.app.string2CString(id)) +} diff --git a/v2/internal/ffenestri/ffenestri_darwin.c b/v2/internal/ffenestri/ffenestri_darwin.c new file mode 100644 index 000000000..1bdb49176 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_darwin.c @@ -0,0 +1,1817 @@ + +#ifdef FFENESTRI_DARWIN + +#include "ffenestri_darwin.h" +#include "menu_darwin.h" +#include "contextmenus_darwin.h" +#include "traymenustore_darwin.h" +#include "traymenu_darwin.h" + +// References to assets +#include "assets.h" +extern const unsigned char runtime; + +// Dialog icons +extern const unsigned char *defaultDialogIcons[]; +#include "userdialogicons.h" + +// MAIN DEBUG FLAG +int debug; + +// A cache for all our dialog icons +struct hashmap_s dialogIconCache; + +// Dispatch Method +typedef void (^dispatchMethod)(void); + +// Message Dialog +void MessageDialog(struct Application *app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton); + +TrayMenuStore *TrayMenuStoreSingleton; + +// dispatch will execute the given `func` pointer +void dispatch(dispatchMethod func) { + dispatch_async(dispatch_get_main_queue(), func); +} +// yes command simply returns YES! +BOOL yes(id self, SEL cmd) +{ + return YES; +} + +// no command simply returns NO! +BOOL no(id self, SEL cmd) +{ + return NO; +} + +// Prints a hashmap entry +int hashmap_log(void *const context, struct hashmap_element_s *const e) { + printf("%s: %p ", (char*)e->key, e->data); + return 0; +} + +void filelog(const char *message) { + FILE *fp = fopen("/tmp/wailslog.txt", "ab"); + if (fp != NULL) + { + fputs(message, fp); + fclose(fp); + } +} + +// The delegate class for tray menus +Class trayMenuDelegateClass; + +// Utility function to visualise a hashmap +void dumpHashmap(const char *name, struct hashmap_s *hashmap) { + printf("%s = { ", name); + if (0!=hashmap_iterate_pairs(hashmap, hashmap_log, NULL)) { + fprintf(stderr, "Failed to dump hashmap entries\n"); + } + printf("}\n"); +} + +extern void messageFromWindowCallback(const char *); +typedef void (*ffenestriCallback)(const char *); + +void HideMouse() { + msg_reg(c("NSCursor"), s("hide")); +} + +void ShowMouse() { + msg_reg(c("NSCursor"), s("unhide")); +} + +OSVersion getOSVersion() { + id processInfo = msg_reg(c("NSProcessInfo"), s("processInfo")); + return GET_OSVERSION(processInfo); +} + +struct Application { + + // Cocoa data + id application; + id delegate; + id windowDelegate; + id mainWindow; + id wkwebview; + id manager; + id config; + id mouseEvent; + id mouseDownMonitor; + id mouseUpMonitor; + int activationPolicy; + id pool; + + // Window Data + const char *title; + int width; + int height; + int minWidth; + int minHeight; + int maxWidth; + int maxHeight; + int resizable; + int devtools; + int fullscreen; + CGFloat red; + CGFloat green; + CGFloat blue; + CGFloat alpha; + int webviewIsTranparent; + const char *appearance; + int decorations; + int logLevel; + int hideWindowOnClose; + + // Features + int frame; + int startHidden; + int maximised; + int titlebarAppearsTransparent; + int hideTitle; + int hideTitleBar; + int fullSizeContent; + int useToolBar; + int hideToolbarSeparator; + int WindowIsTranslucent; + int hasURLHandlers; + const char *startupURL; + + // Menu + Menu *applicationMenu; + + // Context Menus + ContextMenuStore *contextMenuStore; + + // Callback + ffenestriCallback sendMessageToBackend; + + // Bindings + const char *bindings; + + // shutting down flag + bool shuttingDown; + + // Running flag + bool running; + +}; + +// Debug works like sprintf but mutes if the global debug flag is true +// Credit: https://stackoverflow.com/a/20639708 + +#define MAXMESSAGE 1024*10 +char logbuffer[MAXMESSAGE]; + +void Debug(struct Application *app, const char *message, ... ) { + if ( debug ) { + const char *temp = concat("LTFfenestri (C) | ", message); + va_list args; + va_start(args, message); + vsnprintf(logbuffer, MAXMESSAGE, temp, args); + app->sendMessageToBackend(&logbuffer[0]); + MEMFREE(temp); + va_end(args); + } +} + +void Error(struct Application *app, const char *message, ... ) { + const char *temp = concat("LEFfenestri (C) | ", message); + va_list args; + va_start(args, message); + vsnprintf(logbuffer, MAXMESSAGE, temp, args); + app->sendMessageToBackend(&logbuffer[0]); + MEMFREE(temp); + va_end(args); +} + +void Fatal(struct Application *app, const char *message, ... ) { + const char *temp = concat("LFFfenestri (C) | ", message); + va_list args; + va_start(args, message); + vsnprintf(logbuffer, MAXMESSAGE, temp, args); + app->sendMessageToBackend(&logbuffer[0]); + MEMFREE(temp); + va_end(args); +} + +// Requires NSString input EG lookupStringConstant(str("NSFontAttributeName")) +void* lookupStringConstant(id constantName) { + void ** dataPtr = CFBundleGetDataPointerForName(CFBundleGetBundleWithIdentifier((CFStringRef)str("com.apple.AppKit")), (CFStringRef) constantName); + return (dataPtr ? *dataPtr : nil); +} + +bool isRetina(struct Application *app) { + CGFloat scale = GET_BACKINGSCALEFACTOR(app->mainWindow); + if( (int)scale == 1 ) { + return false; + } + return true; +} + +void TitlebarAppearsTransparent(struct Application* app) { + app->titlebarAppearsTransparent = 1; +} + +void HideTitle(struct Application *app) { + app->hideTitle = 1; +} + +void HideTitleBar(struct Application *app) { + app->hideTitleBar = 1; +} + +void HideToolbarSeparator(struct Application *app) { + app->hideToolbarSeparator = 1; +} + +void UseToolbar(struct Application *app) { + app->useToolBar = 1; +} + +// WebviewIsTransparent will make the webview transparent +// revealing the Cocoa window underneath +void WebviewIsTransparent(struct Application *app) { + app->webviewIsTranparent = 1; +} + +// SetAppearance will set the window's Appearance to the +// given value +void SetAppearance(struct Application *app, const char *appearance) { + app->appearance = appearance; +} + + +void applyWindowColour(struct Application *app) { + // Apply the colour only if the window has been created + if( app->mainWindow != NULL ) { + ON_MAIN_THREAD( + id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"), + (CGFloat)app->red / (CGFloat)255.0, + (CGFloat)app->green / (CGFloat)255.0, + (CGFloat)app->blue / (CGFloat)255.0, + (CGFloat)app->alpha / (CGFloat)255.0); + msg_id(app->mainWindow, s("setBackgroundColor:"), colour); + ); + } +} + +void SetColour(struct Application *app, int red, int green, int blue, int alpha) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + app->red = (CGFloat)red; + app->green = (CGFloat)green; + app->blue = (CGFloat)blue; + app->alpha = (CGFloat)alpha; + + applyWindowColour(app); +} + +void FullSizeContent(struct Application *app) { + app->fullSizeContent = 1; +} + +void Hide(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + msg_reg(app->mainWindow, s("orderOut:")); + ); +} + +void Show(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + msg_id(app->mainWindow, s("makeKeyAndOrderFront:"), NULL); + msg_bool(app->application, s("activateIgnoringOtherApps:"), YES); + ); +} + +void WindowIsTranslucent(struct Application *app) { + app->WindowIsTranslucent = 1; +} + +// Sends messages to the backend +void messageHandler(id self, SEL cmd, id contentController, id message) { + struct Application *app = (struct Application *)objc_getAssociatedObject( + self, "application"); + const char *name = (const char *)msg_reg(msg_reg(message, s("name")), s("UTF8String")); + if( strcmp(name, "error") == 0 ) { + printf("There was a Javascript error. Please open the devtools for more information.\n"); + // Show app if we are in debug mode + if( debug ) { + Show(app); + MessageDialog(app, "", "error", "Javascript Error", "There was a Javascript error. Please open the devtools for more information.", "", "", "", "","","",""); + } + } else if( strcmp(name, "completed") == 0) { + // Delete handler + msg_id(app->manager, s("removeScriptMessageHandlerForName:"), str("completed")); + + // TODO: Notify backend we're ready and get them to call back for the Show() + if (app->startHidden == 0) { + Show(app); + } + + // TODO: Check this actually does reduce flicker + ((id(*)(id, SEL, id, id))objc_msgSend)(app->config, s("setValue:forKey:"), msg_bool(c("NSNumber"), s("numberWithBool:"), 0), str("suppressesIncrementalRendering")); + + // We are now running! + app->running = true; + + + // Notify backend we are ready (system startup) + const char *readyMessage = "SS"; + if( app->startupURL == NULL ) { + app->sendMessageToBackend("SS"); + return; + } + readyMessage = concat("SS", app->startupURL); + app->sendMessageToBackend(readyMessage); + MEMFREE(readyMessage); + + } else if( strcmp(name, "windowDrag") == 0 ) { + // Guard against null events + if( app->mouseEvent != NULL ) { + HideMouse(); + ON_MAIN_THREAD( + msg_id(app->mainWindow, s("performWindowDragWithEvent:"), app->mouseEvent); + ); + } + } else if( strcmp(name, "contextMenu") == 0 ) { + + // Did we get a context menu selector? + if( message == NULL) { + return; + } + + const char *contextMenuMessage = cstr(msg_reg(message, s("body"))); + + if( contextMenuMessage == NULL ) { + Debug(app, "EMPTY CONTEXT MENU MESSAGE!!\n"); + return; + } + + // Parse the message + JsonNode *contextMenuMessageJSON = json_decode(contextMenuMessage); + if( contextMenuMessageJSON == NULL ) { + Debug(app, "Error decoding context menu message: %s", contextMenuMessage); + return; + } + + // Get menu ID + JsonNode *contextMenuIDNode = json_find_member(contextMenuMessageJSON, "id"); + if( contextMenuIDNode == NULL ) { + Debug(app, "Error decoding context menu ID: %s", contextMenuMessage); + json_delete(contextMenuMessageJSON); + return; + } + if( contextMenuIDNode->tag != JSON_STRING ) { + Debug(app, "Error decoding context menu ID (Not a string): %s", contextMenuMessage); + json_delete(contextMenuMessageJSON); + return; + } + + // Get menu Data + JsonNode *contextMenuDataNode = json_find_member(contextMenuMessageJSON, "data"); + if( contextMenuDataNode == NULL ) { + Debug(app, "Error decoding context menu data: %s", contextMenuMessage); + json_delete(contextMenuMessageJSON); + return; + } + if( contextMenuDataNode->tag != JSON_STRING ) { + Debug(app, "Error decoding context menu data (Not a string): %s", contextMenuMessage); + json_delete(contextMenuMessageJSON); + return; + } + + // We need to copy these as the JSON node will be destroyed on this thread and the + // string data will become corrupt. These need to be freed by the context menu code. + const char* contextMenuID = STRCOPY(contextMenuIDNode->string_); + const char* contextMenuData = STRCOPY(contextMenuDataNode->string_); + + ON_MAIN_THREAD( + ShowContextMenu(app->contextMenuStore, app->mainWindow, contextMenuID, contextMenuData); + ); + + json_delete(contextMenuMessageJSON); + + } else { + // const char *m = (const char *)msg(msg(message, s("body")), s("UTF8String")); + const char *m = cstr(msg_reg(message, s("body"))); + app->sendMessageToBackend(m); + } +} + +// closeWindow is called when the close button is pressed +void closeWindow(id self, SEL cmd, id sender) { + struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application"); + app->sendMessageToBackend("WC"); +} + +bool isDarkMode(struct Application *app) { + id userDefaults = msg_reg(c("NSUserDefaults"), s("standardUserDefaults")); + const char *mode = cstr(msg_id(userDefaults, s("stringForKey:"), str("AppleInterfaceStyle"))); + return ( mode != NULL && strcmp(mode, "Dark") == 0 ); +} + +void ExecJS(struct Application *app, const char *js) { + ON_MAIN_THREAD( + ((id(*)(id, SEL, id, id))objc_msgSend)(app->wkwebview, + s("evaluateJavaScript:completionHandler:"), + str(js), + NULL); + ); +} + +void willFinishLaunching(id self, SEL cmd, id sender) { + struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application"); + // If there are URL Handlers, register a listener for them + if( app->hasURLHandlers ) { + id eventManager = msg_reg(c("NSAppleEventManager"), s("sharedAppleEventManager")); + ((id(*)(id, SEL, id, SEL, int, int))objc_msgSend)(eventManager, s("setEventHandler:andSelector:forEventClass:andEventID:"), self, s("getUrl:withReplyEvent:"), kInternetEventClass, kAEGetURL); + } + messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}"); +} + +void emitThemeChange(struct Application *app) { + bool currentThemeIsDark = isDarkMode(app); + if (currentThemeIsDark) { + messageFromWindowCallback("Ej{\"name\":\"wails:system:themechange\",\"data\":[true]}"); + } else { + messageFromWindowCallback("Ej{\"name\":\"wails:system:themechange\",\"data\":[false]}"); + } +} + +void themeChanged(id self, SEL cmd, id sender) { + struct Application *app = (struct Application *)objc_getAssociatedObject( + self, "application"); +// emitThemeChange(app); + bool currentThemeIsDark = isDarkMode(app); + if ( currentThemeIsDark ) { + ExecJS(app, "window.wails.Events.Emit( 'wails:system:themechange', true );"); + } else { + ExecJS(app, "window.wails.Events.Emit( 'wails:system:themechange', false );"); + } +} + +int releaseNSObject(void *const context, struct hashmap_element_s *const e) { + msg_reg(e->data, s("release")); + return -1; +} + +void destroyContextMenus(struct Application *app) { + DeleteContextMenuStore(app->contextMenuStore); +} + +void freeDialogIconCache(struct Application *app) { + // Release the dialog cache images + if( hashmap_num_entries(&dialogIconCache) > 0 ) { + if (0!=hashmap_iterate_pairs(&dialogIconCache, releaseNSObject, NULL)) { + Fatal(app, "failed to release hashmap entries!"); + } + } + + //Free radio groups hashmap + hashmap_destroy(&dialogIconCache); +} + +void DestroyApplication(struct Application *app) { + app->shuttingDown = true; + Debug(app, "Destroying Application"); + + // Free the bindings + if (app->bindings != NULL) { + MEMFREE(app->bindings); + } else { + Debug(app, "Almost a double free for app->bindings"); + } + + if( app->startupURL != NULL ) { + MEMFREE(app->startupURL); + } + + // Remove mouse monitors + if( app->mouseDownMonitor != NULL ) { + msg_id( c("NSEvent"), s("removeMonitor:"), app->mouseDownMonitor); + } + if( app->mouseUpMonitor != NULL ) { + msg_id( c("NSEvent"), s("removeMonitor:"), app->mouseUpMonitor); + } + + // Delete the application menu if we have one + if( app->applicationMenu != NULL ) { + DeleteMenu(app->applicationMenu); + } + + // Delete the tray menu store + DeleteTrayMenuStore(TrayMenuStoreSingleton); + + // Delete the context menu store + DeleteContextMenuStore(app->contextMenuStore); + + // Destroy the context menus + destroyContextMenus(app); + + // Free dialog icon cache + freeDialogIconCache(app); + + // Unload the tray Icons + UnloadTrayIcons(); + + // Remove script handlers + msg_id(app->manager, s("removeScriptMessageHandlerForName:"), str("contextMenu")); + msg_id(app->manager, s("removeScriptMessageHandlerForName:"), str("windowDrag")); + msg_id(app->manager, s("removeScriptMessageHandlerForName:"), str("external")); + msg_id(app->manager, s("removeScriptMessageHandlerForName:"), str("error")); + + // Close main window + if( app->windowDelegate != NULL ) { + msg_reg(app->windowDelegate, s("release")); + msg_id(app->mainWindow, s("setDelegate:"), NULL); + } + +// msg(app->mainWindow, s("close")); + + + Debug(app, "Finished Destroying Application"); +} + +// SetTitle sets the main window title to the given string +void SetTitle(struct Application *app, const char *title) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + Debug(app, "SetTitle Called"); + ON_MAIN_THREAD( + msg_id(app->mainWindow, s("setTitle:"), str(title)); + ); +} + +void ToggleFullscreen(struct Application *app) { + ON_MAIN_THREAD( + app->fullscreen = !app->fullscreen; + MAIN_WINDOW_CALL("toggleFullScreen:"); + ); +} + +bool isFullScreen(struct Application *app) { + long mask = (long)msg_reg(app->mainWindow, s("styleMask")); + bool result = (mask & NSWindowStyleMaskFullscreen) == NSWindowStyleMaskFullscreen; + return result; +} + +// Fullscreen sets the main window to be fullscreen +void Fullscreen(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + Debug(app, "Fullscreen Called"); + if( ! isFullScreen(app) ) { + ToggleFullscreen(app); + } +} + +// UnFullscreen resets the main window after a fullscreen +void UnFullscreen(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + Debug(app, "UnFullscreen Called"); + if( isFullScreen(app) ) { + ToggleFullscreen(app); + } +} + +void Center(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + Debug(app, "Center Called"); + ON_MAIN_THREAD( + MAIN_WINDOW_CALL("center"); + ); +} + +void ToggleMaximise(struct Application *app) { + ON_MAIN_THREAD( + app->maximised = !app->maximised; + MAIN_WINDOW_CALL("zoom:"); + ); +} + +void Maximise(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + if( app->maximised == 0) { + ToggleMaximise(app); + } +} + +void Unmaximise(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + if( app->maximised == 1) { + ToggleMaximise(app); + } +} + +void Minimise(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + MAIN_WINDOW_CALL("miniaturize:"); + ); + } +void Unminimise(struct Application *app) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + MAIN_WINDOW_CALL("deminiaturize:"); + ); +} + +id getCurrentScreen(struct Application *app) { + id screen = NULL; + screen = msg_reg(app->mainWindow, s("screen")); + if( screen == NULL ) { + screen = msg_reg(c("NSScreen"), u("mainScreen")); + } + return screen; +} + +void dumpFrame(struct Application *app, const char *message, CGRect frame) { + Debug(app, message); + Debug(app, "origin.x %f", frame.origin.x); + Debug(app, "origin.y %f", frame.origin.y); + Debug(app, "size.width %f", frame.size.width); + Debug(app, "size.height %f", frame.size.height); +} + +void SetSize(struct Application *app, int width, int height) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + id screen = getCurrentScreen(app); + + // Get the rect for the window + CGRect frame = GET_FRAME(app->mainWindow); + + // Credit: https://github.com/patr0nus/DeskGap/blob/73c0ac9f2c73f55b6e81f64f6673a7962b5719cd/lib/src/platform/mac/util/NSScreen%2BGeometry.m + frame.origin.y = (frame.origin.y + frame.size.height) - (float)height; + frame.size.width = (float)width; + frame.size.height = (float)height; + + ((id(*)(id, SEL, CGRect, BOOL, BOOL))objc_msgSend)(app->mainWindow, s("setFrame:display:animate:"), frame, 1, 0); + ); +} + +void SetPosition(struct Application *app, int x, int y) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + id screen = getCurrentScreen(app); + CGRect screenFrame = GET_FRAME(screen); + CGRect windowFrame = GET_FRAME(app->mainWindow); + + windowFrame.origin.x = screenFrame.origin.x + (float)x; + windowFrame.origin.y = (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y; + ((id(*)(id, SEL, CGRect, BOOL, BOOL))objc_msgSend)(app->mainWindow, s("setFrame:display:animate:"), windowFrame, 1, 0); + ); +} + +void processDialogButton(id alert, char *buttonTitle, char *cancelButton, char *defaultButton) { + // If this button is set + if( STR_HAS_CHARS(buttonTitle) ) { + id button = msg_id(alert, s("addButtonWithTitle:"), str(buttonTitle)); + if ( STREQ( buttonTitle, defaultButton) ) { + msg_id(button, s("setKeyEquivalent:"), str("\r")); + } + if ( STREQ( buttonTitle, cancelButton) ) { + msg_id(button, s("setKeyEquivalent:"), str("\033")); + } + } +} + +void MessageDialog(struct Application *app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + id alert = ALLOC_INIT("NSAlert"); + char *dialogType = type; + char *dialogIcon = type; + + // Default to info type + if( dialogType == NULL ) { + dialogType = "info"; + } + + // Set the dialog style + if( STREQ(dialogType, "info") || STREQ(dialogType, "question") ) { + msg_uint(alert, s("setAlertStyle:"), NSAlertStyleInformational); + } else if( STREQ(dialogType, "warning") ) { + msg_uint(alert, s("setAlertStyle:"), NSAlertStyleWarning); + } else if( STREQ(dialogType, "error") ) { + msg_uint(alert, s("setAlertStyle:"), NSAlertStyleCritical); + } + + // Set title if given + if( strlen(title) > 0 ) { + msg_id(alert, s("setMessageText:"), str(title)); + } + + // Set message if given + if( strlen(message) > 0) { + msg_id(alert, s("setInformativeText:"), str(message)); + } + + // Process buttons + processDialogButton(alert, button1, cancelButton, defaultButton); + processDialogButton(alert, button2, cancelButton, defaultButton); + processDialogButton(alert, button3, cancelButton, defaultButton); + processDialogButton(alert, button4, cancelButton, defaultButton); + + // Check for custom dialog icon + if( strlen(icon) > 0 ) { + dialogIcon = icon; + } + + // TODO: move dialog icons + methods to own file + + // Determine what dialog icon we are looking for + id dialogImage = NULL; + // Look for `name-theme2x` first + char *themeIcon = concat(dialogIcon, (isDarkMode(app) ? "-dark" : "-light") ); + if( isRetina(app) ) { + char *dialogIcon2x = concat(themeIcon, "2x"); + dialogImage = hashmap_get(&dialogIconCache, dialogIcon2x, strlen(dialogIcon2x)); +// if (dialogImage != NULL ) printf("Using %s\n", dialogIcon2x); + MEMFREE(dialogIcon2x); + + // Now look for non-themed icon `name2x` + if ( dialogImage == NULL ) { + dialogIcon2x = concat(dialogIcon, "2x"); + dialogImage = hashmap_get(&dialogIconCache, dialogIcon2x, strlen(dialogIcon2x)); +// if (dialogImage != NULL ) printf("Using %s\n", dialogIcon2x); + MEMFREE(dialogIcon2x); + } + } + + // If we don't have a retina icon, try the 1x name-theme icon + if( dialogImage == NULL ) { + dialogImage = hashmap_get(&dialogIconCache, themeIcon, strlen(themeIcon)); +// if (dialogImage != NULL ) printf("Using %s\n", themeIcon); + } + + // Free the theme icon memory + MEMFREE(themeIcon); + + // Finally try the name itself + if( dialogImage == NULL ) { + dialogImage = hashmap_get(&dialogIconCache, dialogIcon, strlen(dialogIcon)); +// if (dialogImage != NULL ) printf("Using %s\n", dialogIcon); + } + + if (dialogImage != NULL ) { + msg_id(alert, s("setIcon:"), dialogImage); + } + + // Run modal + char *buttonPressed; + long response = (long)msg_reg(alert, s("runModal")); + if( response == NSAlertFirstButtonReturn ) { + buttonPressed = button1; + } + else if( response == NSAlertSecondButtonReturn ) { + buttonPressed = button2; + } + else if( response == NSAlertThirdButtonReturn ) { + buttonPressed = button3; + } + else { + buttonPressed = button4; + } + + if ( STR_HAS_CHARS(callbackID) ) { + // Construct callback message. Format "DM|" + const char *callback = concat("DM", callbackID); + const char *header = concat(callback, "|"); + const char *responseMessage = concat(header, buttonPressed); + + // Send message to backend + app->sendMessageToBackend(responseMessage); + + // Free memory + MEMFREE(header); + MEMFREE(callback); + MEMFREE(responseMessage); + } + ); +} + +// OpenDialog opens a dialog to select files/directories +void OpenDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + Debug(app, "OpenDialog Called with callback id: %s", callbackID); + + // Create an open panel + ON_MAIN_THREAD( + + // Create the dialog + id dialog = msg_reg(c("NSOpenPanel"), s("openPanel")); + + // Valid but appears to do nothing.... :/ + msg_id(dialog, s("setTitle:"), str(title)); + + // Filters + if( filters != NULL && strlen(filters) > 0) { + id filterString = msg_id_id(str(filters), s("stringByReplacingOccurrencesOfString:withString:"), str("*."), str("")); + filterString = msg_id_id(filterString, s("stringByReplacingOccurrencesOfString:withString:"), str(" "), str("")); + id filterList = msg_id(filterString, s("componentsSeparatedByString:"), str(",")); + msg_id(dialog, s("setAllowedFileTypes:"), filterList); + } else { + msg_bool(dialog, s("setAllowsOtherFileTypes:"), YES); + } + + // Default Directory + if( defaultDir != NULL && strlen(defaultDir) > 0 ) { + msg_id(dialog, s("setDirectoryURL:"), url(defaultDir)); + } + + // Default Filename + if( defaultFilename != NULL && strlen(defaultFilename) > 0 ) { + msg_id(dialog, s("setNameFieldStringValue:"), str(defaultFilename)); + } + + // Setup Options + msg_bool(dialog, s("setCanChooseFiles:"), allowFiles); + msg_bool(dialog, s("setCanChooseDirectories:"), allowDirs); + msg_bool(dialog, s("setAllowsMultipleSelection:"), allowMultiple); + msg_bool(dialog, s("setShowsHiddenFiles:"), showHiddenFiles); + msg_bool(dialog, s("setCanCreateDirectories:"), canCreateDirectories); + msg_bool(dialog, s("setResolvesAliases:"), resolvesAliases); + msg_bool(dialog, s("setTreatsFilePackagesAsDirectories:"), treatPackagesAsDirectories); + + // Setup callback handler + ((id(*)(id, SEL, id, void (^)(id)))objc_msgSend)(dialog, s("beginSheetModalForWindow:completionHandler:"), app->mainWindow, ^(id result) { + + // Create the response JSON object + JsonNode *response = json_mkarray(); + + // If the user selected some files + if( result == (id)1 ) { + // Grab the URLs returned + id urls = msg_reg(dialog, s("URLs")); + + // Iterate over all the selected files + long noOfResults = (long)msg_reg(urls, s("count")); + for( int index = 0; index < noOfResults; index++ ) { + + // Extract the filename + id url = msg_int(urls, s("objectAtIndex:"), index); + const char *filename = (const char *)msg_reg(msg_reg(url, s("path")), s("UTF8String")); + + // Add the the response array + json_append_element(response, json_mkstring(filename)); + } + } + + // Create JSON string and free json memory + char *encoded = json_stringify(response, ""); + json_delete(response); + + // Construct callback message. Format "D|" + const char *callback = concat("DO", callbackID); + const char *header = concat(callback, "|"); + const char *responseMessage = concat(header, encoded); + + // Send message to backend + app->sendMessageToBackend(responseMessage); + + // Free memory + MEMFREE(header); + MEMFREE(callback); + MEMFREE(responseMessage); + }); + + msg_id( c("NSApp"), s("runModalForWindow:"), app->mainWindow); + ); +} + +// SaveDialog opens a dialog to select files/directories +void SaveDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + Debug(app, "SaveDialog Called with callback id: %s", callbackID); + + // Create an open panel + ON_MAIN_THREAD( + + // Create the dialog + id dialog = msg_reg(c("NSSavePanel"), s("savePanel")); + + // Valid but appears to do nothing.... :/ + msg_id(dialog, s("setTitle:"), str(title)); + + // Filters + if( filters != NULL && strlen(filters) > 0) { + id filterString = msg_id_id(str(filters), s("stringByReplacingOccurrencesOfString:withString:"), str("*."), str("")); + filterString = msg_id_id(filterString, s("stringByReplacingOccurrencesOfString:withString:"), str(" "), str("")); + id filterList = msg_id(filterString, s("componentsSeparatedByString:"), str(",")); + msg_id(dialog, s("setAllowedFileTypes:"), filterList); + } else { + msg_bool(dialog, s("setAllowsOtherFileTypes:"), YES); + } + + // Default Directory + if( defaultDir != NULL && strlen(defaultDir) > 0 ) { + msg_id(dialog, s("setDirectoryURL:"), url(defaultDir)); + } + + // Default Filename + if( defaultFilename != NULL && strlen(defaultFilename) > 0 ) { + msg_id(dialog, s("setNameFieldStringValue:"), str(defaultFilename)); + } + + // Setup Options + msg_bool(dialog, s("setShowsHiddenFiles:"), showHiddenFiles); + msg_bool(dialog, s("setCanCreateDirectories:"), canCreateDirectories); + msg_bool(dialog, s("setTreatsFilePackagesAsDirectories:"), treatPackagesAsDirectories); + + // Setup callback handler + ((id(*)(id, SEL, id, void (^)(id)))objc_msgSend)(dialog, s("beginSheetModalForWindow:completionHandler:"), app->mainWindow, ^(id result) { + + // Default is blank + const char *filename = ""; + + // If the user selected some files + if( result == (id)1 ) { + // Grab the URL returned + id url = msg_reg(dialog, s("URL")); + filename = (const char *)msg_reg(msg_reg(url, s("path")), s("UTF8String")); + } + + // Construct callback message. Format "DS|" + const char *callback = concat("DS", callbackID); + const char *header = concat(callback, "|"); + const char *responseMessage = concat(header, filename); + + // Send message to backend + app->sendMessageToBackend(responseMessage); + + // Free memory + MEMFREE(header); + MEMFREE(callback); + MEMFREE(responseMessage); + }); + + msg_id( c("NSApp"), s("runModalForWindow:"), app->mainWindow); + ); +} + +const char *invoke = "window.wailsInvoke=function(message){window.webkit.messageHandlers.external.postMessage(message);};window.wailsDrag=function(message){window.webkit.messageHandlers.windowDrag.postMessage(message);};window.wailsContextMenuMessage=function(message){window.webkit.messageHandlers.contextMenu.postMessage(message);};"; + +// DisableFrame disables the window frame +void DisableFrame(struct Application *app) +{ + app->frame = 0; +} + +void setMinMaxSize(struct Application *app) +{ + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + if (app->maxHeight > 0 && app->maxWidth > 0) + { + ((id(*)(id, SEL, CGSize))objc_msgSend)(app->mainWindow, s("setMaxSize:"), CGSizeMake(app->maxWidth, app->maxHeight)); + } + if (app->minHeight > 0 && app->minWidth > 0) + { + ((id(*)(id, SEL, CGSize))objc_msgSend)(app->mainWindow, s("setMinSize:"), CGSizeMake(app->minWidth, app->minHeight)); + } + + // Calculate if window needs resizing + int newWidth = app->width; + int newHeight = app->height; + + if (app->maxWidth > 0 && app->width > app->maxWidth) newWidth = app->maxWidth; + if (app->minWidth > 0 && app->width < app->minWidth) newWidth = app->minWidth; + if (app->maxHeight > 0 && app->height > app->maxHeight ) newHeight = app->maxHeight; + if (app->minHeight > 0 && app->height < app->minHeight ) newHeight = app->minHeight; + + // If we have any change, resize window + if ( newWidth != app->width || newHeight != app->height ) { + SetSize(app, newWidth, newHeight); + } +} + +void SetMinWindowSize(struct Application *app, int minWidth, int minHeight) +{ + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + app->minWidth = minWidth; + app->minHeight = minHeight; + + // Apply if the window is created + if( app->mainWindow != NULL ) { + ON_MAIN_THREAD( + setMinMaxSize(app); + ); + } +} + +void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight) +{ + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + app->maxWidth = maxWidth; + app->maxHeight = maxHeight; + + // Apply if the window is created + if( app->mainWindow != NULL ) { + ON_MAIN_THREAD( + setMinMaxSize(app); + ); + } +} + + +void SetDebug(void *applicationPointer, int flag) { + debug = flag; +} + + + +// AddContextMenu sets the context menu map for this application +void AddContextMenu(struct Application *app, const char *contextMenuJSON) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + ON_MAIN_THREAD ( + AddContextMenuToStore(app->contextMenuStore, contextMenuJSON); + ); +} + +void UpdateContextMenu(struct Application *app, const char* contextMenuJSON) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + ON_MAIN_THREAD( + UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON); + ); +} + +void AddTrayMenu(struct Application *app, const char *trayMenuJSON) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + AddTrayMenuToStore(TrayMenuStoreSingleton, trayMenuJSON); + ); +} + +void SetTrayMenu(struct Application *app, const char* trayMenuJSON) { + + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + UpdateTrayMenuInStore(TrayMenuStoreSingleton, trayMenuJSON); + ); +} + +void DeleteTrayMenuByID(struct Application *app, const char *id) { + ON_MAIN_THREAD( + DeleteTrayMenuInStore(TrayMenuStoreSingleton, id); + ); +} + +void UpdateTrayMenuLabel(struct Application* app, const char* JSON) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + UpdateTrayMenuLabelInStore(TrayMenuStoreSingleton, JSON); + ); +} + + +void SetBindings(struct Application *app, const char *bindings) { + const char* temp = concat("window.wailsbindings = \"", bindings); + const char* jscall = concat(temp, "\";"); + MEMFREE(temp); + app->bindings = jscall; +} + +void makeWindowBackgroundTranslucent(struct Application *app) { + id contentView = msg_reg(app->mainWindow, s("contentView")); + id effectView = msg_reg(c("NSVisualEffectView"), s("alloc")); + CGRect bounds = GET_BOUNDS(contentView); + effectView = ((id(*)(id, SEL, CGRect))objc_msgSend)(effectView, s("initWithFrame:"), bounds); + + msg_int(effectView, s("setAutoresizingMask:"), NSViewWidthSizable | NSViewHeightSizable); + msg_int(effectView, s("setBlendingMode:"), NSVisualEffectBlendingModeBehindWindow); + msg_int(effectView, s("setState:"), NSVisualEffectStateActive); + ((id(*)(id, SEL, id, int, id))objc_msgSend)(contentView, s("addSubview:positioned:relativeTo:"), effectView, NSWindowBelow, NULL); +} + +void enableBoolConfig(id config, const char *setting) { + ((id(*)(id, SEL, id, id))objc_msgSend)(msg_reg(config, s("preferences")), s("setValue:forKey:"), msg_bool(c("NSNumber"), s("numberWithBool:"), 1), str(setting)); +} + +void disableBoolConfig(id config, const char *setting) { + ((id(*)(id, SEL, id, id))objc_msgSend)(msg_reg(config, s("preferences")), s("setValue:forKey:"), msg_bool(c("NSNumber"), s("numberWithBool:"), 0), str(setting)); +} + +void processDecorations(struct Application *app) { + + int decorations = 0; + + if (app->frame == 1 ) { + if( app->hideTitleBar == 0) { + decorations |= NSWindowStyleMaskTitled; + } + decorations |= NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + } + + if (app->resizable) { + decorations |= NSWindowStyleMaskResizable; + } + + if (app->fullscreen) { + decorations |= NSWindowStyleMaskFullscreen; + } + + if( app->fullSizeContent || app->frame == 0) { + decorations |= NSWindowStyleMaskFullSizeContentView; + } + + app->decorations = decorations; +} + +void createApplication(struct Application *app) { + id application = msg_reg(c("NSApplication"), s("sharedApplication")); + app->application = application; + msg_int(application, s("setActivationPolicy:"), app->activationPolicy); +} + +void DarkModeEnabled(struct Application *app, const char *callbackID) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + ON_MAIN_THREAD( + const char *result = isDarkMode(app) ? "T" : "F"; + + // Construct callback message. Format "SD|" + const char *callback = concat("SD", callbackID); + const char *header = concat(callback, "|"); + const char *responseMessage = concat(header, result); + // Send message to backend + app->sendMessageToBackend(responseMessage); + + // Free memory + MEMFREE(header); + MEMFREE(callback); + MEMFREE(responseMessage); + ); +} + +void getURL(id self, SEL selector, id event, id replyEvent) { + struct Application *app = (struct Application *)objc_getAssociatedObject(self, "application"); + id desc = msg_int(event, s("paramDescriptorForKeyword:"), keyDirectObject); + id url = msg_reg(desc, s("stringValue")); + const char* curl = cstr(url); + if( curl == NULL ) { + return; + } + + // If this was an incoming URL, but we aren't running yet + // save it to return when we complete + if( app->running != true ) { + app->startupURL = STRCOPY(curl); + return; + } + + const char* message = concat("UC", curl); + messageFromWindowCallback(message); + MEMFREE(message); +} + +void openURLs(id self, SEL selector, id event) { + filelog("\n\nI AM HERE!!!!!\n\n"); +} + + +void createDelegate(struct Application *app) { + + // Define delegate + Class appDelegate = objc_allocateClassPair((Class) c("NSResponder"), "AppDelegate", 0); + class_addProtocol(appDelegate, objc_getProtocol("NSTouchBarProvider")); + + class_addMethod(appDelegate, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@"); + class_addMethod(appDelegate, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@"); + + // All Menu Items use a common callback + class_addMethod(appDelegate, s("menuItemCallback:"), (IMP)menuItemCallback, "v@:@"); + + // If there are URL Handlers, register the callback method + if( app->hasURLHandlers ) { + class_addMethod(appDelegate, s("getUrl:withReplyEvent:"), (IMP) getURL, "i@:@@"); + } + + // Script handler + class_addMethod(appDelegate, s("userContentController:didReceiveScriptMessage:"), (IMP) messageHandler, "v@:@@"); + objc_registerClassPair(appDelegate); + + // Create delegate + id delegate = msg_reg((id)appDelegate, s("new")); + objc_setAssociatedObject(delegate, "application", (id)app, OBJC_ASSOCIATION_ASSIGN); + + // Theme change listener + class_addMethod(appDelegate, s("themeChanged:"), (IMP) themeChanged, "v@:@@"); + + // Get defaultCenter + id defaultCenter = msg_reg(c("NSDistributedNotificationCenter"), s("defaultCenter")); + ((id(*)(id, SEL, id, SEL, id, id))objc_msgSend)(defaultCenter, s("addObserver:selector:name:object:"), delegate, s("themeChanged:"), str("AppleInterfaceThemeChangedNotification"), NULL); + + app->delegate = delegate; + + msg_id(app->application, s("setDelegate:"), delegate); +} + +bool windowShouldClose(id self, SEL cmd, id sender) { + msg_reg(sender, s("orderOut:")); + return false; +} + +bool windowShouldExit(id self, SEL cmd, id sender) { + msg_reg(sender, s("orderOut:")); + messageFromWindowCallback("WC"); + return false; +} + +void createMainWindow(struct Application *app) { + // Create main window + id mainWindow = ALLOC("NSWindow"); + mainWindow = ((id(*)(id, SEL, CGRect, int, int, BOOL))objc_msgSend)(mainWindow, s("initWithContentRect:styleMask:backing:defer:"), + CGRectMake(0, 0, app->width, app->height), app->decorations, NSBackingStoreBuffered, NO); + msg_reg(mainWindow, s("autorelease")); + + // Set Appearance + if( app->appearance != NULL ) { + msg_id(mainWindow, s("setAppearance:"), + msg_id(c("NSAppearance"), s("appearanceNamed:"), str(app->appearance)) + ); + } + + // Set Title appearance + msg_bool(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO); + msg_int(mainWindow, s("setTitleVisibility:"), app->hideTitle); + + // Create window delegate to override windowShouldClose + Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "WindowDelegate", 0); + bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSWindowDelegate")); + if( app->hideWindowOnClose ) { + class_replaceMethod(delegateClass, s("windowShouldClose:"), (IMP) windowShouldClose, "v@:@"); + } else { + class_replaceMethod(delegateClass, s("windowShouldClose:"), (IMP) windowShouldExit, "v@:@"); + } + app->windowDelegate = msg_reg((id)delegateClass, s("new")); + msg_id(mainWindow, s("setDelegate:"), app->windowDelegate); + + app->mainWindow = mainWindow; +} + +const char* getInitialState(struct Application *app) { + const char *result = ""; + if( isDarkMode(app) ) { + result = "window.wails.System.IsDarkMode.set(true);"; + } else { + result = "window.wails.System.IsDarkMode.set(false);"; + } + char buffer[999]; + snprintf(&buffer[0], sizeof(buffer), "window.wails.System.LogLevel.set(%d);", app->logLevel); + result = concat(result, &buffer[0]); + Debug(app, "initialstate = %s", result); + return result; +} + +void parseMenuRole(struct Application *app, id parentMenu, JsonNode *item) { + const char *roleName = item->string_; + + if ( STREQ(roleName, "appMenu") ) { + createDefaultAppMenu(parentMenu); + return; + } + if ( STREQ(roleName, "editMenu")) { + createDefaultEditMenu(parentMenu); + return; + } + if ( STREQ(roleName, "hide")) { + addMenuItem(parentMenu, "Hide Window", "hide:", "h", FALSE); + return; + } + if ( STREQ(roleName, "hideothers")) { + id hideOthers = addMenuItem(parentMenu, "Hide Others", "hideOtherApplications:", "h", FALSE); + msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand)); + return; + } + if ( STREQ(roleName, "unhide")) { + addMenuItem(parentMenu, "Show All", "unhideAllApplications:", "", FALSE); + return; + } + if ( STREQ(roleName, "front")) { + addMenuItem(parentMenu, "Bring All to Front", "arrangeInFront:", "", FALSE); + return; + } + if ( STREQ(roleName, "undo")) { + addMenuItem(parentMenu, "Undo", "undo:", "z", FALSE); + return; + } + if ( STREQ(roleName, "redo")) { + addMenuItem(parentMenu, "Redo", "redo:", "y", FALSE); + return; + } + if ( STREQ(roleName, "cut")) { + addMenuItem(parentMenu, "Cut", "cut:", "x", FALSE); + return; + } + if ( STREQ(roleName, "copy")) { + addMenuItem(parentMenu, "Copy", "copy:", "c", FALSE); + return; + } + if ( STREQ(roleName, "paste")) { + addMenuItem(parentMenu, "Paste", "paste:", "v", FALSE); + return; + } + if ( STREQ(roleName, "delete")) { + addMenuItem(parentMenu, "Delete", "delete:", "", FALSE); + return; + } + if( STREQ(roleName, "pasteandmatchstyle")) { + id pasteandmatchstyle = addMenuItem(parentMenu, "Paste and Match Style", "pasteandmatchstyle:", "v", FALSE); + msg_int(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand)); + } + if ( STREQ(roleName, "selectall")) { + addMenuItem(parentMenu, "Select All", "selectAll:", "a", FALSE); + return; + } + if ( STREQ(roleName, "minimize")) { + addMenuItem(parentMenu, "Minimize", "miniaturize:", "m", FALSE); + return; + } + if ( STREQ(roleName, "zoom")) { + addMenuItem(parentMenu, "Zoom", "performZoom:", "", FALSE); + return; + } + if ( STREQ(roleName, "quit")) { + addMenuItem(parentMenu, "Quit", "terminate:", "q", FALSE); + return; + } + if ( STREQ(roleName, "togglefullscreen")) { + addMenuItem(parentMenu, "Toggle Full Screen", "toggleFullScreen:", "f", FALSE); + return; + } + +} + +void dumpMemberList(const char *name, id *memberList) { + void *member = memberList[0]; + int count = 0; + printf("%s = %p -> [ ", name, memberList); + while( member != NULL ) { + printf("%p ", member); + count = count + 1; + member = memberList[count]; + } + printf("]\n"); +} + +// updateMenu replaces the current menu with the given one +void updateMenu(struct Application *app, const char *menuAsJSON) { + Debug(app, "Menu is now: %s", menuAsJSON); + ON_MAIN_THREAD ( + DeleteMenu(app->applicationMenu); + Menu* newMenu = NewApplicationMenu(menuAsJSON); + id menu = GetMenu(newMenu); + app->applicationMenu = newMenu; + msg_id(msg_reg(c("NSApplication"), s("sharedApplication")), s("setMainMenu:"), menu); + ); +} + +// SetApplicationMenu sets the initial menu for the application +void SetApplicationMenu(struct Application *app, const char *menuAsJSON) { + // Guard against calling during shutdown + if( app->shuttingDown ) return; + + if ( app->applicationMenu == NULL ) { + app->applicationMenu = NewApplicationMenu(menuAsJSON); + return; + } + + // Update menu + ON_MAIN_THREAD ( + updateMenu(app, menuAsJSON); + ); +} + +void processDialogIcons(struct hashmap_s *hashmap, const unsigned char *dialogIcons[]) { + + unsigned int count = 0; + while( 1 ) { + const unsigned char *name = dialogIcons[count++]; + if( name == 0x00 ) { + break; + } + const unsigned char *lengthAsString = dialogIcons[count++]; + if( name == 0x00 ) { + break; + } + const unsigned char *data = dialogIcons[count++]; + if( data == 0x00 ) { + break; + } + int length = atoi((const char *)lengthAsString); + + // Create the icon and add to the hashmap + id imageData = ((id(*)(id, SEL, const unsigned char *, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), data, length); + id dialogImage = ALLOC("NSImage"); + msg_reg(dialogImage, s("autorelease")); + msg_id(dialogImage, s("initWithData:"), imageData); + hashmap_put(hashmap, (const char *)name, strlen((const char *)name), dialogImage); + } + +} + +void processUserDialogIcons(struct Application *app) { + + // Allocate the Dialog icon hashmap + if( 0 != hashmap_create((const unsigned)4, &dialogIconCache)) { + // Couldn't allocate map + Fatal(app, "Not enough memory to allocate dialogIconCache!"); + return; + } + + processDialogIcons(&dialogIconCache, defaultDialogIcons); + processDialogIcons(&dialogIconCache, userDialogIcons); + +} + +void TrayMenuWillOpen(id self, SEL selector, id menu) { + // Extract tray menu id from menu + id trayMenuIDStr = objc_getAssociatedObject(menu, "trayMenuID"); + const char* trayMenuID = cstr(trayMenuIDStr); + const char *message = concat("Mo", trayMenuID); + messageFromWindowCallback(message); + MEMFREE(message); +} + +void TrayMenuDidClose(id self, SEL selector, id menu) { + // Extract tray menu id from menu + id trayMenuIDStr = objc_getAssociatedObject(menu, "trayMenuID"); + const char* trayMenuID = cstr(trayMenuIDStr); + const char *message = concat("Mc", trayMenuID); + messageFromWindowCallback(message); + MEMFREE(message); +} + +void createTrayMenuDelegate() { + // Define delegate + trayMenuDelegateClass = objc_allocateClassPair((Class) c("NSObject"), "MenuDelegate", 0); + class_addProtocol(trayMenuDelegateClass, objc_getProtocol("NSMenuDelegate")); + class_addMethod(trayMenuDelegateClass, s("menuWillOpen:"), (IMP) TrayMenuWillOpen, "v@:@"); + class_addMethod(trayMenuDelegateClass, s("menuDidClose:"), (IMP) TrayMenuDidClose, "v@:@"); + + // Script handler + objc_registerClassPair(trayMenuDelegateClass); +} + + +void Run(struct Application *app, int argc, char **argv) { + + // Process window decorations + processDecorations(app); + + // Create the application + createApplication(app); + + // Define delegate + createDelegate(app); + + // Define tray delegate + createTrayMenuDelegate(); + + // Create the main window + createMainWindow(app); + + // Create Content View + id contentView = msg_reg( ALLOC("NSView"), s("init") ); + msg_id(app->mainWindow, s("setContentView:"), contentView); + + // Set the main window title + SetTitle(app, app->title); + + // Center Window + Center(app); + + // Set Colour + applyWindowColour(app); + + // Process translucency + if (app->WindowIsTranslucent) { + makeWindowBackgroundTranslucent(app); + } + + // We set it to be invisible by default. It will become visible when everything has initialised + msg_bool(app->mainWindow, s("setIsVisible:"), NO); + + // Setup webview + id config = msg_reg(c("WKWebViewConfiguration"), s("new")); + ((id(*)(id, SEL, id, id))objc_msgSend)(config, s("setValue:forKey:"), msg_bool(c("NSNumber"), s("numberWithBool:"), 1), str("suppressesIncrementalRendering")); + if (app->devtools) { + Debug(app, "Enabling devtools"); + enableBoolConfig(config, "developerExtrasEnabled"); + } + app->config = config; + + id manager = msg_reg(config, s("userContentController")); + msg_id_id(manager, s("addScriptMessageHandler:name:"), app->delegate, str("external")); + msg_id_id(manager, s("addScriptMessageHandler:name:"), app->delegate, str("completed")); + msg_id_id(manager, s("addScriptMessageHandler:name:"), app->delegate, str("error")); + app->manager = manager; + + id wkwebview = msg_reg(c("WKWebView"), s("alloc")); + app->wkwebview = wkwebview; + + ((id(*)(id, SEL, CGRect, id))objc_msgSend)(wkwebview, s("initWithFrame:configuration:"), CGRectMake(0, 0, 0, 0), config); + + msg_id(contentView, s("addSubview:"), wkwebview); + msg_int(wkwebview, s("setAutoresizingMask:"), NSViewWidthSizable | NSViewHeightSizable); + CGRect contentViewBounds = GET_BOUNDS(contentView); + ((id(*)(id, SEL, CGRect))objc_msgSend)(wkwebview, s("setFrame:"), contentViewBounds ); + + // Disable damn smart quotes + // Credit: https://stackoverflow.com/a/31640511 + id userDefaults = msg_reg(c("NSUserDefaults"), s("standardUserDefaults")); + ((id(*)(id, SEL, BOOL, id))objc_msgSend)(userDefaults, s("setBool:forKey:"), false, str("NSAutomaticQuoteSubstitutionEnabled")); + + // Setup drag message handler + msg_id_id(manager, s("addScriptMessageHandler:name:"), app->delegate, str("windowDrag")); + // Add mouse event hooks + app->mouseDownMonitor = ((id(*)(id, SEL, int, id (^)(id)))objc_msgSend)(c("NSEvent"), u("addLocalMonitorForEventsMatchingMask:handler:"), NSEventMaskLeftMouseDown, ^(id incomingEvent) { + // Make sure the mouse click was in the window, not the tray + id window = msg_reg(incomingEvent, s("window")); + if (window == app->mainWindow) { + app->mouseEvent = incomingEvent; + } + return incomingEvent; + }); + app->mouseUpMonitor = ((id(*)(id, SEL, int, id (^)(id)))objc_msgSend)(c("NSEvent"), u("addLocalMonitorForEventsMatchingMask:handler:"), NSEventMaskLeftMouseUp, ^(id incomingEvent) { + app->mouseEvent = NULL; + ShowMouse(); + return incomingEvent; + }); + + // Setup context menu message handler + msg_id_id(manager, s("addScriptMessageHandler:name:"), app->delegate, str("contextMenu")); + + // Toolbar + if( app->useToolBar ) { + Debug(app, "Setting Toolbar"); + id toolbar = msg_reg(c("NSToolbar"),s("alloc")); + msg_id(toolbar, s("initWithIdentifier:"), str("wails.toolbar")); + msg_reg(toolbar, s("autorelease")); + + // Separator + if( app->hideToolbarSeparator ) { + msg_bool(toolbar, s("setShowsBaselineSeparator:"), NO); + } + + msg_id(app->mainWindow, s("setToolbar:"), toolbar); + } + + // Fix up resizing + if (app->resizable == 0) { + app->minHeight = app->maxHeight = app->height; + app->minWidth = app->maxWidth = app->width; + } + setMinMaxSize(app); + + // Load HTML + id html = msg_id(c("NSURL"), s("URLWithString:"), str((const char*)assets[0])); + msg_id(wkwebview, s("loadRequest:"), msg_id(c("NSURLRequest"), s("requestWithURL:"), html)); + + Debug(app, "Loading Internal Code"); + // We want to evaluate the internal code plus runtime before the assets + const char *temp = concat(invoke, app->bindings); + const char *internalCode = concat(temp, (const char*)&runtime); + MEMFREE(temp); + + // Add code that sets up the initial state, EG: State Stores. + const char *initialState = getInitialState(app); + temp = concat(internalCode, initialState); + MEMFREE(initialState); + MEMFREE(internalCode); + internalCode = temp; + + // Loop over assets and build up one giant Mother Of All Evals + int index = 1; + while(1) { + // Get next asset pointer + const unsigned char *asset = assets[index]; + + // If we have no more assets, break + if (asset == 0x00) { + break; + } + + temp = concat(internalCode, (const char *)asset); + MEMFREE(internalCode); + internalCode = temp; + index++; + }; + + // Disable context menu if not in debug mode + if( debug != 1 ) { + temp = concat(internalCode, "wails._.DisableDefaultContextMenu();"); + MEMFREE(internalCode); + internalCode = temp; + } + + // class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "@@:@"); + // Include callback after evaluation + temp = concat(internalCode, "webkit.messageHandlers.completed.postMessage(true);"); + MEMFREE(internalCode); + internalCode = temp; + + // const char *viewportScriptString = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); meta.setAttribute('initial-scale', '1.0'); meta.setAttribute('maximum-scale', '1.0'); meta.setAttribute('minimum-scale', '1.0'); meta.setAttribute('user-scalable', 'no'); document.getElementsByTagName('head')[0].appendChild(meta);"; + // ExecJS(app, viewportScriptString); + + + // This evaluates the MOAE once the Dom has finished loading + msg_id(manager, + s("addUserScript:"), + ((id(*)(id, SEL, id, int, int))objc_msgSend)(msg_reg(c("WKUserScript"), s("alloc")), + s("initWithSource:injectionTime:forMainFrameOnly:"), + str(internalCode), + 1, + 1)); + + + // Emit theme change event to notify of current system them + emitThemeChange(app); + + // If we want the webview to be transparent... + if( app->webviewIsTranparent == 1 ) { + ((id(*)(id, SEL, id, id))objc_msgSend)(wkwebview, s("setValue:forKey:"), msg_bool(c("NSNumber"), s("numberWithBool:"), 0), str("drawsBackground")); + } + + // If we have an application menu, process it + if( app->applicationMenu != NULL ) { + id menu = GetMenu(app->applicationMenu); + msg_id(msg_reg(c("NSApplication"), s("sharedApplication")), s("setMainMenu:"), menu); + } + + // Setup initial trays + ShowTrayMenusInStore(TrayMenuStoreSingleton); + + // Process dialog icons + processUserDialogIcons(app); + + // Finally call run + Debug(app, "Run called"); + msg_reg(app->application, s("run")); + + DestroyApplication(app); + + MEMFREE(internalCode); +} + +void SetActivationPolicy(struct Application* app, int policy) { + app->activationPolicy = policy; +} + +void HasURLHandlers(struct Application* app) { + app->hasURLHandlers = 1; +} + +// Quit will stop the cocoa application and free up all the memory +// used by the application +void Quit(struct Application *app) { + Debug(app, "Quit Called"); + msg_id(app->application, s("stop:"), NULL); +} + +id createImageFromBase64Data(const char *data, bool isTemplateImage) { + id nsdata = ALLOC("NSData"); + id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(nsdata, s("initWithBase64EncodedString:options:"), str(data), 0); + + // If it's not valid base64 data, use the broken image + if ( imageData == NULL ) { + imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(nsdata, s("initWithBase64EncodedString:options:"), str(BrokenImage), 0); + } + id result = ALLOC("NSImage"); + msg_reg(result, s("autorelease")); + msg_id(result, s("initWithData:"), imageData); + msg_reg(nsdata, s("release")); + msg_reg(imageData, s("release")); + + if( isTemplateImage ) { + msg_bool(result, s("setTemplate:"), YES); + } + + return result; +} + +void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) { + + // Load the tray icons + LoadTrayIcons(); + + // Setup main application struct + struct Application *result = malloc(sizeof(struct Application)); + result->title = title; + result->width = width; + result->height = height; + result->minWidth = 0; + result->minHeight = 0; + result->maxWidth = 0; + result->maxHeight = 0; + result->resizable = resizable; + result->devtools = devtools; + result->fullscreen = fullscreen; + result->maximised = 0; + result->startHidden = startHidden; + result->decorations = 0; + result->logLevel = logLevel; + result->hideWindowOnClose = hideWindowOnClose; + + result->mainWindow = NULL; + result->mouseEvent = NULL; + result->mouseDownMonitor = NULL; + result->mouseUpMonitor = NULL; + + // Features + result->frame = 1; + result->hideTitle = 0; + result->hideTitleBar = 0; + result->fullSizeContent = 0; + result->useToolBar = 0; + result->hideToolbarSeparator = 0; + result->appearance = NULL; + result->WindowIsTranslucent = 0; + + // Window data + result->delegate = NULL; + + // Menu + result->applicationMenu = NULL; + + // Tray + TrayMenuStoreSingleton = NewTrayMenuStore(); + + // Context Menus + result->contextMenuStore = NewContextMenuStore(); + + // Window delegate + result->windowDelegate = NULL; + + // Window Appearance + result->titlebarAppearsTransparent = 0; + result->webviewIsTranparent = 0; + + result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback; + + result->shuttingDown = false; + + result->activationPolicy = NSApplicationActivationPolicyRegular; + + result->hasURLHandlers = 0; + + result->startupURL = NULL; + + result->running = false; + + result->pool = msg_reg(c("NSAutoreleasePool"), s("new")); + + return (void*) result; +} + + +#endif diff --git a/v2/internal/ffenestri/ffenestri_darwin.go b/v2/internal/ffenestri/ffenestri_darwin.go new file mode 100644 index 000000000..02d7471b1 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_darwin.go @@ -0,0 +1,99 @@ +package ffenestri + +/* +#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1 +#cgo darwin LDFLAGS: -framework WebKit -framework CoreFoundation -lobjc + +#include "ffenestri.h" +#include "ffenestri_darwin.h" + +*/ +import "C" + +func (a *Application) processPlatformSettings() error { + + mac := a.config.Mac + titlebar := mac.TitleBar + + // HideTitle + if titlebar.HideTitle { + C.HideTitle(a.app) + } + + // HideTitleBar + if titlebar.HideTitleBar { + C.HideTitleBar(a.app) + } + + // Full Size Content + if titlebar.FullSizeContent { + C.FullSizeContent(a.app) + } + + // Toolbar + if titlebar.UseToolbar { + C.UseToolbar(a.app) + } + + if titlebar.HideToolbarSeparator { + C.HideToolbarSeparator(a.app) + } + + if titlebar.TitlebarAppearsTransparent { + C.TitlebarAppearsTransparent(a.app) + } + + // Process window Appearance + if mac.Appearance != "" { + C.SetAppearance(a.app, a.string2CString(string(mac.Appearance))) + } + + // Set activation policy + C.SetActivationPolicy(a.app, C.int(mac.ActivationPolicy)) + + // Check if the webview should be transparent + if mac.WebviewIsTransparent { + C.WebviewIsTransparent(a.app) + } + + // Check if window should be translucent + if mac.WindowIsTranslucent { + C.WindowIsTranslucent(a.app) + } + + // Process menu + //applicationMenu := options.GetApplicationMenu(a.config) + applicationMenu := a.menuManager.GetApplicationMenuJSON() + if applicationMenu != "" { + C.SetApplicationMenu(a.app, a.string2CString(applicationMenu)) + } + + // Process tray + trays, err := a.menuManager.GetTrayMenus() + if err != nil { + return err + } + if trays != nil { + for _, tray := range trays { + C.AddTrayMenu(a.app, a.string2CString(tray)) + } + } + + // Process context menus + contextMenus, err := a.menuManager.GetContextMenus() + if err != nil { + return err + } + if contextMenus != nil { + for _, contextMenu := range contextMenus { + C.AddContextMenu(a.app, a.string2CString(contextMenu)) + } + } + + // Process URL Handlers + if a.config.Mac.URLHandlers != nil { + C.HasURLHandlers(a.app) + } + + return nil +} diff --git a/v2/internal/ffenestri/ffenestri_darwin.h b/v2/internal/ffenestri/ffenestri_darwin.h new file mode 100644 index 000000000..fd00338a4 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_darwin.h @@ -0,0 +1,154 @@ + +#ifndef FFENESTRI_DARWIN_H +#define FFENESTRI_DARWIN_H + + +#define OBJC_OLD_DISPATCH_PROTOTYPES 1 +#include +#include +#include +#include "json.h" +#include "hashmap.h" +#include "stdlib.h" + +typedef struct { + long maj; + long min; + long patch; +} OSVersion; + +// Macros to make it slightly more sane +#define msg objc_msgSend +#define msg_reg ((id(*)(id, SEL))objc_msgSend) +#define msg_id ((id(*)(id, SEL, id))objc_msgSend) +#define msg_id_id ((id(*)(id, SEL, id, id))objc_msgSend) +#define msg_bool ((id(*)(id, SEL, BOOL))objc_msgSend) +#define msg_int ((id(*)(id, SEL, int))objc_msgSend) +#define msg_uint ((id(*)(id, SEL, unsigned int))objc_msgSend) +#define msg_float ((id(*)(id, SEL, float))objc_msgSend) +#define kInternetEventClass 'GURL' +#define kAEGetURL 'GURL' +#define keyDirectObject '----' + +#define c(str) (id)objc_getClass(str) +#define s(str) sel_registerName(str) +#define u(str) sel_getUid(str) +#define str(input) ((id(*)(id, SEL, const char *))objc_msgSend)(c("NSString"), s("stringWithUTF8String:"), input) +#define strunicode(input) ((id(*)(id, SEL, id, unsigned short))objc_msgSend)(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input) +#define cstr(input) (const char *)msg_reg(input, s("UTF8String")) +#define url(input) msg_id(c("NSURL"), s("fileURLWithPath:"), str(input)) +#define ALLOC(classname) msg_reg(c(classname), s("alloc")) +#define ALLOC_INIT(classname) msg_reg(msg_reg(c(classname), s("alloc")), s("init")) + +#if defined (__aarch64__) +#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("frame")) +#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("bounds")) +#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend)(processInfo, s("operatingSystemVersion")); +#endif + +#if defined (__x86_64__) +#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame")) +#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("bounds")) +#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend_stret)(processInfo, s("operatingSystemVersion")); +#endif + +#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))objc_msgSend)(receiver, s("backingScaleFactor")) + +#define ON_MAIN_THREAD(str) dispatch( ^{ str; } ) +#define MAIN_WINDOW_CALL(str) msg_reg(app->mainWindow, s((str))) + +#define NSBackingStoreBuffered 2 + +#define NSWindowStyleMaskBorderless 0 +#define NSWindowStyleMaskTitled 1 +#define NSWindowStyleMaskClosable 2 +#define NSWindowStyleMaskMiniaturizable 4 +#define NSWindowStyleMaskResizable 8 +#define NSWindowStyleMaskFullscreen 1 << 14 + +#define NSVisualEffectMaterialWindowBackground 12 +#define NSVisualEffectBlendingModeBehindWindow 0 +#define NSVisualEffectStateFollowsWindowActiveState 0 +#define NSVisualEffectStateActive 1 +#define NSVisualEffectStateInactive 2 + +#define NSViewWidthSizable 2 +#define NSViewHeightSizable 16 + +#define NSWindowBelow -1 +#define NSWindowAbove 1 + +#define NSSquareStatusItemLength -2.0 +#define NSVariableStatusItemLength -1.0 + +#define NSWindowTitleHidden 1 +#define NSWindowStyleMaskFullSizeContentView 1 << 15 + +#define NSEventModifierFlagCommand 1 << 20 +#define NSEventModifierFlagOption 1 << 19 +#define NSEventModifierFlagControl 1 << 18 +#define NSEventModifierFlagShift 1 << 17 + +#define NSControlStateValueMixed -1 +#define NSControlStateValueOff 0 +#define NSControlStateValueOn 1 + +#define NSApplicationActivationPolicyRegular 0 +#define NSApplicationActivationPolicyAccessory 1 +#define NSApplicationActivationPolicyProhibited 2 + +// Unbelievably, if the user swaps their button preference +// then right buttons are reported as left buttons +#define NSEventMaskLeftMouseDown 1 << 1 +#define NSEventMaskLeftMouseUp 1 << 2 +#define NSEventMaskRightMouseDown 1 << 3 +#define NSEventMaskRightMouseUp 1 << 4 + +#define NSEventTypeLeftMouseDown 1 +#define NSEventTypeLeftMouseUp 2 +#define NSEventTypeRightMouseDown 3 +#define NSEventTypeRightMouseUp 4 + +#define NSNoImage 0 +#define NSImageOnly 1 +#define NSImageLeft 2 +#define NSImageRight 3 +#define NSImageBelow 4 +#define NSImageAbove 5 +#define NSImageOverlaps 6 + +#define NSAlertStyleWarning 0 +#define NSAlertStyleInformational 1 +#define NSAlertStyleCritical 2 + +#define NSAlertFirstButtonReturn 1000 +#define NSAlertSecondButtonReturn 1001 +#define NSAlertThirdButtonReturn 1002 + +#define BrokenImage "iVBORw0KGgoAAAANSUhEUgAAABAAAAASCAMAAABl5a5YAAABj1BMVEWopan///+koqSWk5P9/v3///////////+AgACMiovz8/PB0fG9z+3i4+WysbGBfX1Erh80rACLiYqBxolEsDhHlDEbqQDDx+CNho7W1tj4+/bw+O3P5Mn4/f/W1tbK6sX////b2dn////////////8/Pz6+vro6Ojj4+P////G1PL////EzNydmp2cmZnd3eDF1PHs8v/o8P/Q3vrS3vfE0vCdmpqZkpr19/3N2vXI1vPH1fOgnqDg6frP3PbCytvHx8irqq6HhIZtuGtjnlZetU1Xs0NWskBNsi7w9v/d6P7w9P3S4Pzr8Pvl7PrY5PrU4PjQ3fjD1Ozo6Om30NjGzNi7ubm34K+UxKmbnaWXlJeUjpSPi4tppF1TtjxSsTf2+f7L2PTr7e3H2+3V7+q+0uXg4OPg4eLR1uG7z+Hg4ODGzODV2N7V1trP5dmxzs65vcfFxMWq0cKxxr+/vr+0s7apxbWaxrCv2qao05+dlp2Uuo2Dn4F8vIB6xnyAoHmAym9zqGpctENLryNFsgoblJpnAAAAKnRSTlP+hP7+5ZRmYgL+/f39/f39/f38/Pz8/Pv69+7j083My8GocnBPTTMWEgjxeITOAAABEklEQVQY0y3KZXuCYBiG4ceYuu7u3nyVAaKOMBBQ7O5Yd3f3fvheDnd9u8/jBkGwNxP6sjOWVQvY/ftrzfT6bd3yEhCnYZqiaYoKiwX/gXkFiHySTcUTLJMsZ9v8nQvgssWYOEKedKpcOO6CUXD5IlGEY5hLUbyDAAZ6HRf1bnkoavOsFQibg+Q4nuNYL+ON5PHD5nBaraRVyxnzGf6BJzUi2QQCQgMyk8tleL7dg1owpJ17D5IkvV100EingeOopPyo6vfAuXF+9hbDTknZCIaUoeK4efKwG4iT6xDewd7imGlid7gGwv37b6Oh9jwaTdOf/Tc1qH7UZVmuP6G5qZfBr9cAGNy4KiDd4tXIs7tS+QO9aUKvPAIKuQAAAABJRU5ErkJggg==" + +struct Application; +int releaseNSObject(void *const context, struct hashmap_element_s *const e); +void TitlebarAppearsTransparent(struct Application* app); +void HideTitle(struct Application* app); +void HideTitleBar(struct Application* app); +void FullSizeContent(struct Application* app); +void UseToolbar(struct Application* app); +void HideToolbarSeparator(struct Application* app); +void DisableFrame(struct Application* app); +void SetAppearance(struct Application* app, const char *); +void WebviewIsTransparent(struct Application* app); +void WindowIsTranslucent(struct Application* app); +void SetTray(struct Application* app, const char *, const char *, const char *); +//void SetContextMenus(struct Application* app, const char *); +void AddTrayMenu(struct Application* app, const char *); + +void SetActivationPolicy(struct Application* app, int policy); + +void* lookupStringConstant(id constantName); + +void HasURLHandlers(struct Application* app); + +id createImageFromBase64Data(const char *data, bool isTemplateImage); + +#endif \ No newline at end of file diff --git a/v2/internal/ffenestri/ffenestri_linux.c b/v2/internal/ffenestri/ffenestri_linux.c new file mode 100644 index 000000000..ab6b6032b --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_linux.c @@ -0,0 +1,995 @@ + +#ifndef __FFENESTRI_LINUX_H__ +#define __FFENESTRI_LINUX_H__ + +#include "common.h" +#include "gtk/gtk.h" +#include "webkit2/webkit2.h" +#include +#include +#include +#include +#include + +// References to assets +extern const unsigned char runtime; + +#include "icon.h" +#include "assets.h" + +// Constants +#define PRIMARY_MOUSE_BUTTON 1 +#define MIDDLE_MOUSE_BUTTON 2 +#define SECONDARY_MOUSE_BUTTON 3 + +// MAIN DEBUG FLAG +int debug; + +// Debug works like sprintf but mutes if the global debug flag is true +// Credit: https://stackoverflow.com/a/20639708 +void Debug(char *message, ...) +{ + if (debug) + { + char *temp = concat("TRACE | Ffenestri (C) | ", message); + message = concat(temp, "\n"); + free(temp); + va_list args; + va_start(args, message); + vprintf(message, args); + free(message); + va_end(args); + } +} + +extern void messageFromWindowCallback(const char *); +typedef void (*ffenestriCallback)(const char *); + +struct Application +{ + + // Gtk Data + GtkApplication *application; + GtkWindow *mainWindow; + GtkWidget *webView; + int signalInvoke; + int signalWindowDrag; + int signalButtonPressed; + int signalButtonReleased; + int signalLoadChanged; + + // Saves the events for the drag mouse button + GdkEventButton *dragButtonEvent; + + // The number of the default drag button + int dragButton; + + // Window Data + const char *title; + char *id; + int width; + int height; + int resizable; + int devtools; + int startHidden; + int fullscreen; + int minWidth; + int minHeight; + int maxWidth; + int maxHeight; + int frame; + + // User Data + char *HTML; + + // Callback + ffenestriCallback sendMessageToBackend; + + // Bindings + const char *bindings; + + // Lock - used for sync operations (Should we be using g_mutex?) + int lock; +}; + +void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden) +{ + // Setup main application struct + struct Application *result = malloc(sizeof(struct Application)); + result->title = title; + result->width = width; + result->height = height; + result->resizable = resizable; + result->devtools = devtools; + result->fullscreen = fullscreen; + result->minWidth = 0; + result->minHeight = 0; + result->maxWidth = 0; + result->maxHeight = 0; + result->frame = 1; + result->startHidden = startHidden; + + // Default drag button is PRIMARY + result->dragButton = PRIMARY_MOUSE_BUTTON; + + result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback; + + // Create a unique ID based on the current unix timestamp + char temp[11]; + sprintf(&temp[0], "%d", (int)time(NULL)); + result->id = concat("wails.app", &temp[0]); + + // Create the main GTK application + GApplicationFlags flags = G_APPLICATION_FLAGS_NONE; + result->application = gtk_application_new(result->id, flags); + + // Return the application struct + return (void *)result; +} + +void DestroyApplication(struct Application *app) +{ + Debug("Destroying Application"); + + g_application_quit(G_APPLICATION(app->application)); + + // Release the GTK ID string + if (app->id != NULL) + { + free(app->id); + app->id = NULL; + } + else + { + Debug("Almost a double free for app->id"); + } + + // Free the bindings + if (app->bindings != NULL) + { + free((void *)app->bindings); + app->bindings = NULL; + } + else + { + Debug("Almost a double free for app->bindings"); + } + + // Disconnect signal handlers + WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView); + g_signal_handler_disconnect(manager, app->signalInvoke); + if( app->frame == 0) { + g_signal_handler_disconnect(manager, app->signalWindowDrag); + g_signal_handler_disconnect(app->webView, app->signalButtonPressed); + g_signal_handler_disconnect(app->webView, app->signalButtonReleased); + } + g_signal_handler_disconnect(app->webView, app->signalLoadChanged); + + // Release the main GTK Application + if (app->application != NULL) + { + g_object_unref(app->application); + app->application = NULL; + } + else + { + Debug("Almost a double free for app->application"); + } + Debug("Finished Destroying Application"); +} + +// Quit will stop the gtk application and free up all the memory +// used by the application +void Quit(struct Application *app) +{ + Debug("Quit Called"); + gtk_window_close((GtkWindow *)app->mainWindow); + g_application_quit((GApplication *)app->application); + DestroyApplication(app); +} + +// SetTitle sets the main window title to the given string +void SetTitle(struct Application *app, const char *title) +{ + gtk_window_set_title(app->mainWindow, title); +} + +// Fullscreen sets the main window to be fullscreen +void Fullscreen(struct Application *app) +{ + gtk_window_fullscreen(app->mainWindow); +} + +// UnFullscreen resets the main window after a fullscreen +void UnFullscreen(struct Application *app) +{ + gtk_window_unfullscreen(app->mainWindow); +} + +void setMinMaxSize(struct Application *app) +{ + GdkGeometry size; + size.min_width = size.min_height = size.max_width = size.max_height = 0; + int flags = 0; + if (app->maxHeight > 0 && app->maxWidth > 0) + { + size.max_height = app->maxHeight; + size.max_width = app->maxWidth; + flags |= GDK_HINT_MAX_SIZE; + } + if (app->minHeight > 0 && app->minWidth > 0) + { + size.min_height = app->minHeight; + size.min_width = app->minWidth; + flags |= GDK_HINT_MIN_SIZE; + } + gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags); +} + +char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAction, char **args) { + GtkFileChooserNative *native; + GtkFileChooserAction action = chooserAction; + gint res; + char *filename; + + char *title = args[0]; + char *filter = args[1]; + + native = gtk_file_chooser_native_new(title, + app->mainWindow, + action, + "_Open", + "_Cancel"); + + GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); + + // If we have filters, process them + if (filter[0] != '\0') { + GtkFileFilter *file_filter = gtk_file_filter_new(); + gchar **filters = g_strsplit(filter, ",", -1); + gint i; + for(i = 0; filters && filters[i]; i++) { + gtk_file_filter_add_pattern(file_filter, filters[i]); + // Debug("Adding filter pattern: %s\n", filters[i]); + } + gtk_file_filter_set_name(file_filter, filter); + gtk_file_chooser_add_filter(chooser, file_filter); + g_strfreev(filters); + } + + res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)); + if (res == GTK_RESPONSE_ACCEPT) + { + filename = gtk_file_chooser_get_filename(chooser); + } + + g_object_unref(native); + + return filename; +} + +// openFileDialogInternal opens a dialog to select a file +// NOTE: The result is a string that will need to be freed! +char *openFileDialogInternal(struct Application *app, char **args) +{ + return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_OPEN, args); +} + +// saveFileDialogInternal opens a dialog to select a file +// NOTE: The result is a string that will need to be freed! +char *saveFileDialogInternal(struct Application *app, char **args) +{ + return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SAVE, args); +} + + +// openDirectoryDialogInternal opens a dialog to select a directory +// NOTE: The result is a string that will need to be freed! +char *openDirectoryDialogInternal(struct Application *app, char **args) +{ + return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, args); +} + +void SetMinWindowSize(struct Application *app, int minWidth, int minHeight) +{ + app->minWidth = minWidth; + app->minHeight = minHeight; +} + +void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight) +{ + app->maxWidth = maxWidth; + app->maxHeight = maxHeight; +} + +// SetColour sets the colour of the webview to the given colour string +void SetColour(struct Application *app, int red, int green, int blue, int alpha) +{ +// GdkRGBA rgba; +// rgba. +// gboolean result = gdk_rgba_parse(&rgba, colourString); +// if (result == FALSE) +// { +// return 0; +// } +// // Debug("Setting webview colour to: %s", colourString); +// webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba); +} + +// DisableFrame disables the window frame +void DisableFrame(struct Application *app) +{ + app->frame = 0; +} + +void syncCallback(GObject *source_object, + GAsyncResult *res, + void *data) +{ + struct Application *app = (struct Application *)data; + app->lock = 0; +} + +void syncEval(struct Application *app, const gchar *script) +{ + + WebKitWebView *webView = (WebKitWebView *)(app->webView); + + // wait for lock to free + while (app->lock == 1) + { + g_main_context_iteration(0, true); + } + // Set lock + app->lock = 1; + + webkit_web_view_run_javascript( + webView, + script, + NULL, syncCallback, (void*)app); + + while (app->lock == 1) + { + g_main_context_iteration(0, true); + } +} + +void asyncEval(WebKitWebView *webView, const gchar *script) +{ + webkit_web_view_run_javascript( + webView, + script, + NULL, NULL, NULL); +} + +typedef void (*dispatchMethod)(struct Application *app, void *); + +struct dispatchData +{ + struct Application *app; + dispatchMethod method; + void *args; +}; + +gboolean executeMethod(gpointer data) +{ + struct dispatchData *d = (struct dispatchData *)data; + (d->method)(d->app, d->args); + g_free(d); + return FALSE; +} + +void ExecJS(struct Application *app, char *js) +{ + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)syncEval; + data->args = js; + data->app = app; + + gdk_threads_add_idle(executeMethod, data); +} + +typedef char *(*dialogMethod)(struct Application *app, void *); + +struct dialogCall +{ + struct Application *app; + dialogMethod method; + void *args; + void *filter; + char *result; + int done; +}; + +gboolean executeMethodWithReturn(gpointer data) +{ + struct dialogCall *d = (struct dialogCall *)data; + + d->result = (d->method)(d->app, d->args); + d->done = 1; + return FALSE; +} + +char *OpenFileDialog(struct Application *app, char *title, char *filter) +{ + struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1); + data->result = NULL; + data->done = 0; + data->method = (dialogMethod)openFileDialogInternal; + const char* dialogArgs[]={ title, filter }; + data->args = dialogArgs; + data->app = app; + + gdk_threads_add_idle(executeMethodWithReturn, data); + + while (data->done == 0) + { + usleep(100000); + } + g_free(data); + return data->result; +} + +char *SaveFileDialog(struct Application *app, char *title, char *filter) +{ + struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1); + data->result = NULL; + data->done = 0; + data->method = (dialogMethod)saveFileDialogInternal; + const char* dialogArgs[]={ title, filter }; + data->args = dialogArgs; + data->app = app; + + gdk_threads_add_idle(executeMethodWithReturn, data); + + while (data->done == 0) + { + usleep(100000); + } + Debug("Dialog done"); + Debug("Result = %s\n", data->result); + + g_free(data); + // Fingers crossed this wasn't freed by g_free above + return data->result; +} + +char *OpenDirectoryDialog(struct Application *app, char *title, char *filter) +{ + struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1); + data->result = NULL; + data->done = 0; + data->method = (dialogMethod)openDirectoryDialogInternal; + const char* dialogArgs[]={ title, filter }; + data->args = dialogArgs; + data->app = app; + + gdk_threads_add_idle(executeMethodWithReturn, data); + + while (data->done == 0) + { + usleep(100000); + } + Debug("Directory Dialog done"); + Debug("Result = %s\n", data->result); + g_free(data); + // Fingers crossed this wasn't freed by g_free above + return data->result; +} + +// Sets the icon to the XPM stored in icon +void setIcon(struct Application *app) +{ + GdkPixbuf *appIcon = gdk_pixbuf_new_from_xpm_data((const char **)icon); + gtk_window_set_icon(app->mainWindow, appIcon); +} + +static void load_finished_cb(WebKitWebView *webView, + WebKitLoadEvent load_event, + struct Application *app) +{ + switch (load_event) + { + // case WEBKIT_LOAD_STARTED: + // /* New load, we have now a provisional URI */ + // // printf("Start downloading %s\n", webkit_web_view_get_uri(web_view)); + // /* Here we could start a spinner or update the + // * location bar with the provisional URI */ + // break; + // case WEBKIT_LOAD_REDIRECTED: + // // printf("Redirected to: %s\n", webkit_web_view_get_uri(web_view)); + // break; + // case WEBKIT_LOAD_COMMITTED: + // /* The load is being performed. Current URI is + // * the final one and it won't change unless a new + // * load is requested or a navigation within the + // * same page is performed */ + // // printf("Loading: %s\n", webkit_web_view_get_uri(web_view)); + // break; + case WEBKIT_LOAD_FINISHED: + /* Load finished, we can now stop the spinner */ + // printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view)); + + // Bindings + Debug("Binding Methods"); + syncEval(app, app->bindings); + + // Setup IPC commands + Debug("Setting up IPC methods"); + const char *invoke = "window.wailsInvoke=function(message){window.webkit.messageHandlers.external.postMessage(message);};window.wailsDrag=function(message){window.webkit.messageHandlers.windowDrag.postMessage(message);};window.wailsContextMenuMessage=function(message){window.webkit.messageHandlers.contextMenu.postMessage(message);};"; + syncEval(app, invoke); + + // Runtime + Debug("Setting up Wails runtime"); + syncEval(app, &runtime); + + // Loop over assets + int index = 1; + while (1) + { + // Get next asset pointer + const char *asset = assets[index]; + + // If we have no more assets, break + if (asset == 0x00) + { + break; + } + + // sync eval the asset + syncEval(app, asset); + index++; + }; + + // Set the icon + setIcon(app); + + // Setup fullscreen + if (app->fullscreen) + { + Debug("Going fullscreen"); + Fullscreen(app); + } + + // Setup resize + gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height); + + if (app->resizable) + { + gtk_window_set_default_size(GTK_WINDOW(app->mainWindow), app->width, app->height); + } + else + { + gtk_widget_set_size_request(GTK_WIDGET(app->mainWindow), app->width, app->height); + gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height); + // Fix the min/max to the window size for good measure + app->minHeight = app->maxHeight = app->height; + app->minWidth = app->maxWidth = app->width; + } + gtk_window_set_resizable(GTK_WINDOW(app->mainWindow), app->resizable ? TRUE : FALSE); + setMinMaxSize(app); + + // Centre by default + gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER); + + // Show window and focus + if( app->startHidden == 0) { + gtk_widget_show_all(GTK_WIDGET(app->mainWindow)); + gtk_widget_grab_focus(app->webView); + } + break; + } +} + +static gboolean disable_context_menu_cb( + WebKitWebView *web_view, + WebKitContextMenu *context_menu, + GdkEvent *event, + WebKitHitTestResult *hit_test_result, + gpointer user_data) +{ + return TRUE; +} + +static void printEvent(const char *message, GdkEventButton *event) +{ + Debug("%s: [button:%d] [x:%f] [y:%f] [time:%d]", + message, + event->button, + event->x_root, + event->y_root, + event->time); +} + + +static void dragWindow(WebKitUserContentManager *contentManager, + WebKitJavascriptResult *result, + struct Application *app) +{ + // If we get this message erroneously, ignore + if (app->dragButtonEvent == NULL) + { + return; + } + + // Ignore non-toplevel widgets + GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(app->webView)); + if (!GTK_IS_WINDOW(window)) + { + return; + } + + // Initiate the drag + printEvent("Starting drag with event", app->dragButtonEvent); + + gtk_window_begin_move_drag(app->mainWindow, + app->dragButton, + app->dragButtonEvent->x_root, + app->dragButtonEvent->y_root, + app->dragButtonEvent->time); + // Clear the event + app->dragButtonEvent = NULL; + + return; +} + +gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, struct Application *app) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton) + { + app->dragButtonEvent = event; + } + return FALSE; +} + +gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Application *app) +{ + if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton) + { + app->dragButtonEvent = NULL; + } + return FALSE; +} + +static void sendMessageToBackend(WebKitUserContentManager *contentManager, + WebKitJavascriptResult *result, + struct Application *app) +{ +#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22 + JSCValue *value = webkit_javascript_result_get_js_value(result); + char *message = jsc_value_to_string(value); +#else + JSGlobalContextRef context = webkit_javascript_result_get_global_context(result); + JSValueRef value = webkit_javascript_result_get_value(result); + JSStringRef js = JSValueToStringCopy(context, value, NULL); + size_t messageSize = JSStringGetMaximumUTF8CStringSize(js); + char *message = g_new(char, messageSize); + JSStringGetUTF8CString(js, message, messageSize); + JSStringRelease(js); +#endif + app->sendMessageToBackend(message); + g_free(message); +} + +void SetDebug(struct Application *app, int flag) +{ + debug = flag; +} + +// getCurrentMonitorGeometry gets the geometry of the monitor +// that the window is mostly on. +GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) { + // Get the monitor that the window is currently on + GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window)); + GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); + GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, gdk_window); + + // Get the geometry of the monitor + GdkRectangle result; + gdk_monitor_get_geometry (monitor,&result); + + return result; +} + +/******************* + * Window Position * + *******************/ + +// Position holds an x/y corrdinate +struct Position { + int x; + int y; +}; + +// Internal call for setting the position of the window. +void setPositionInternal(struct Application *app, struct Position *pos) { + + // Get the monitor geometry + GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow); + + // Move the window relative to the monitor + gtk_window_move(app->mainWindow, m.x + pos->x, m.y + pos->y); + + // Free memory + free(pos); +} + +// SetPosition sets the position of the window to the given x/y +// coordinates. The x/y values are relative to the monitor +// the window is mostly on. +void SetPosition(struct Application *app, int x, int y) { + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)setPositionInternal; + struct Position *pos = malloc(sizeof(struct Position)); + pos->x = x; + pos->y = y; + data->args = pos; + data->app = app; + + gdk_threads_add_idle(executeMethod, data); +} + +/*************** + * Window Size * + ***************/ + +// Size holds a width/height +struct Size { + int width; + int height; +}; + +// Internal call for setting the size of the window. +void setSizeInternal(struct Application *app, struct Size *size) { + gtk_window_resize(app->mainWindow, size->width, size->height); + free(size); +} + +// SetSize sets the size of the window to the given width/height +void SetSize(struct Application *app, int width, int height) { + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)setSizeInternal; + struct Size *size = malloc(sizeof(struct Size)); + size->width = width; + size->height = height; + data->args = size; + data->app = app; + + gdk_threads_add_idle(executeMethod, data); +} + + +// centerInternal will center the main window on the monitor it is mostly in +void centerInternal(struct Application *app) +{ + // Get the geometry of the monitor + GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow); + + // Get the window width/height + int windowWidth, windowHeight; + gtk_window_get_size(app->mainWindow, &windowWidth, &windowHeight); + + // Place the window at the center of the monitor + gtk_window_move(app->mainWindow, ((m.width - windowWidth) / 2) + m.x, ((m.height - windowHeight) / 2) + m.y); +} + +// Center the window +void Center(struct Application *app) { + + // Setup a call to centerInternal on the main thread + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)centerInternal; + data->app = app; + + // Add call to main thread + gdk_threads_add_idle(executeMethod, data); +} + +// hideInternal hides the main window +void hideInternal(struct Application *app) { + gtk_widget_hide (GTK_WIDGET(app->mainWindow)); +} + +// Hide places the hideInternal method onto the main thread for execution +void Hide(struct Application *app) { + + // Setup a call to hideInternal on the main thread + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)hideInternal; + data->app = app; + + // Add call to main thread + gdk_threads_add_idle(executeMethod, data); +} + +// showInternal shows the main window +void showInternal(struct Application *app) { + gtk_widget_show_all(GTK_WIDGET(app->mainWindow)); + gtk_widget_grab_focus(app->webView); +} + +// Show places the showInternal method onto the main thread for execution +void Show(struct Application *app) { + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)showInternal; + data->app = app; + + gdk_threads_add_idle(executeMethod, data); +} + + +// maximiseInternal maximises the main window +void maximiseInternal(struct Application *app) { + gtk_window_maximize(GTK_WINDOW(app->mainWindow)); +} + +// Maximise places the maximiseInternal method onto the main thread for execution +void Maximise(struct Application *app) { + + // Setup a call to maximiseInternal on the main thread + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)maximiseInternal; + data->app = app; + + // Add call to main thread + gdk_threads_add_idle(executeMethod, data); +} + +// unmaximiseInternal unmaximises the main window +void unmaximiseInternal(struct Application *app) { + gtk_window_unmaximize(GTK_WINDOW(app->mainWindow)); +} + +// Unmaximise places the unmaximiseInternal method onto the main thread for execution +void Unmaximise(struct Application *app) { + + // Setup a call to unmaximiseInternal on the main thread + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)unmaximiseInternal; + data->app = app; + + // Add call to main thread + gdk_threads_add_idle(executeMethod, data); +} + + +void DarkModeEnabled(struct Application* app, char *callbackID) {} +void SetApplicationMenu(struct Application* app, const char *menuJSON) {} +void AddTrayMenu(struct Application* app, const char *menuTrayJSON) {} +void SetTrayMenu(struct Application* app, const char *menuTrayJSON) {} +void DeleteTrayMenuByID(struct Application* app, const char *id) {} +void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {} +void AddContextMenu(struct Application* app, char *contextMenuJSON) {} +void UpdateContextMenu(struct Application* app, char *contextMenuJSON) {} +void WebviewIsTransparent(struct Application* app) {} +void WindowIsTranslucent(struct Application* app) {} +void OpenDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) {} +void SaveDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {} +void MessageDialog(struct Application* app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {} + + +// minimiseInternal minimises the main window +void minimiseInternal(struct Application *app) { + gtk_window_iconify(app->mainWindow); +} + +// Minimise places the minimiseInternal method onto the main thread for execution +void Minimise(struct Application *app) { + + // Setup a call to minimiseInternal on the main thread + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)minimiseInternal; + data->app = app; + + // Add call to main thread + gdk_threads_add_idle(executeMethod, data); +} + +// unminimiseInternal unminimises the main window +void unminimiseInternal(struct Application *app) { + gtk_window_present(app->mainWindow); +} + +// Unminimise places the unminimiseInternal method onto the main thread for execution +void Unminimise(struct Application *app) { + + // Setup a call to unminimiseInternal on the main thread + struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1); + data->method = (dispatchMethod)unminimiseInternal; + data->app = app; + + // Add call to main thread + gdk_threads_add_idle(executeMethod, data); +} + + +void SetBindings(struct Application *app, const char *bindings) +{ + const char *temp = concat("window.wailsbindings = \"", bindings); + const char *jscall = concat(temp, "\";"); + free((void *)temp); + app->bindings = jscall; +} + +// This is called when the close button on the window is pressed +gboolean close_button_pressed(GtkWidget *widget, + GdkEvent *event, + struct Application *app) +{ + app->sendMessageToBackend("WC"); // Window Close + return TRUE; +} + +static void setupWindow(struct Application *app) +{ + + // Create the window + GtkWidget *mainWindow = gtk_application_window_new(app->application); + // Save reference + app->mainWindow = GTK_WINDOW(mainWindow); + + // Setup frame + gtk_window_set_decorated((GtkWindow *)mainWindow, app->frame); + + // Setup title + gtk_window_set_title(GTK_WINDOW(mainWindow), app->title); + + // Setup script handler + WebKitUserContentManager *contentManager = webkit_user_content_manager_new(); + + // Setup the invoke handler + webkit_user_content_manager_register_script_message_handler(contentManager, "external"); + app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app); + + // Setup the window drag handler if this is a frameless app + if ( app->frame == 0 ) { + webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag"); + app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app); + // Setup the mouse handlers + app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app); + app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app); + } + GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager); + + // Save reference + app->webView = webView; + + // Add the webview to the window + gtk_container_add(GTK_CONTAINER(mainWindow), webView); + + + // Load default HTML + app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app); + + // Load the user's HTML + // assets[0] is the HTML because the asset array is bundled like that by convention + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webView), assets[0]); + + // Check if we want to enable the dev tools + if (app->devtools) + { + WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView)); + // webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); + webkit_settings_set_enable_developer_extras(settings, true); + } + else + { + g_signal_connect(G_OBJECT(webView), "context-menu", G_CALLBACK(disable_context_menu_cb), app); + } + + // Listen for close button signal + g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app); +} + +static void activate(GtkApplication* _, struct Application *app) +{ + setupWindow(app); +} + +void Run(struct Application *app, int argc, char **argv) +{ + g_signal_connect(app->application, "activate", G_CALLBACK(activate), app); + g_application_run(G_APPLICATION(app->application), argc, argv); +} + +#endif diff --git a/v2/internal/ffenestri/ffenestri_linux.go b/v2/internal/ffenestri/ffenestri_linux.go new file mode 100644 index 000000000..ca7abe7a1 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_linux.go @@ -0,0 +1,17 @@ +package ffenestri + +/* +#cgo linux CFLAGS: -DFFENESTRI_LINUX=1 +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 + + +#include "ffenestri.h" +#include "ffenestri_linux.h" + +*/ +import "C" + +func (a *Application) processPlatformSettings() error { + + return nil +} diff --git a/v2/internal/ffenestri/ffenestri_linux.h b/v2/internal/ffenestri/ffenestri_linux.h new file mode 100644 index 000000000..26902d334 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_linux.h @@ -0,0 +1,6 @@ + +#ifndef FFENESTRI_LINUX_H +#define FFENESTRI_LINUX_H + + +#endif \ No newline at end of file diff --git a/v2/internal/ffenestri/ffenestri_windows.cpp b/v2/internal/ffenestri/ffenestri_windows.cpp new file mode 100644 index 000000000..91500774f --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_windows.cpp @@ -0,0 +1,906 @@ +// Some code may be inspired by or directly used from Webview (c) zserge. +// License included in README.md + +#include "ffenestri_windows.h" +#include "shellscalingapi.h" +#include "wv2ComHandler_windows.h" +#include +#include +#include +#include +#include +#include "windows/WebView2.h" +#include +#include "effectstructs_windows.h" +#include + +int debug = 0; +DWORD mainThread; + +#define WS_EX_NOREDIRECTIONBITMAP 0x00200000L + +// --- Assets +extern const unsigned char runtime; +extern const unsigned char *defaultDialogIcons[]; + +// dispatch will execute the given `func` pointer +void dispatch(dispatchFunction func) { + PostThreadMessage(mainThread, WM_APP, 0, (LPARAM) new dispatchFunction(func)); +} + +void processKeyPress(UINT key) { + // Get state of Control + bool controlPressed = GetKeyState(VK_CONTROL) >> 15 != 0; + bool altPressed = GetKeyState(VK_MENU) >> 15 != 0; + bool shiftPressed = GetKeyState(VK_SHIFT) >> 15 != 0; + + // Save the modifier keys + BYTE modState = 0; + if ( GetKeyState(VK_CONTROL) >> 15 != 0 ) { modState |= 1; } + if ( GetKeyState(VK_MENU) >> 15 != 0 ) { modState |= 2; } + if ( GetKeyState(VK_SHIFT) >> 15 != 0 ) { modState |= 4; } + if ( GetKeyState(VK_LWIN) >> 15 != 0 ) { modState |= 8; } + if ( GetKeyState(VK_RWIN) >> 15 != 0 ) { modState |= 8; } + + // Notify app of keypress + handleKeypressInGo(key, modState); +} + + +LPWSTR cstrToLPWSTR(const char *cstr) { + int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, NULL , 0 ); + wchar_t* wstr = new wchar_t[wchars_num+1]; + MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, wstr , wchars_num ); + return wstr; +} + +// Credit: https://stackoverflow.com/a/9842450 +char* LPWSTRToCstr(LPWSTR input) { + int length = WideCharToMultiByte(CP_UTF8, 0, input, -1, 0, 0, NULL, NULL); + char* output = new char[length]; + WideCharToMultiByte(CP_UTF8, 0, input, -1, output , length, NULL, NULL); + return output; +} + + +// Credit: https://building.enlyze.com/posts/writing-win32-apps-like-its-2020-part-3/ +typedef int (__cdecl *PGetDpiForMonitor)(HMONITOR, MONITOR_DPI_TYPE,UINT*,UINT*); +void getDPIForWindow(struct Application *app) +{ + HMODULE hShcore = LoadLibraryW(L"shcore"); + if (hShcore) + { + PGetDpiForMonitor pGetDpiForMonitor = reinterpret_cast(GetProcAddress(hShcore, "GetDpiForMonitor")); + if (pGetDpiForMonitor) + { + HMONITOR hMonitor = MonitorFromWindow(app->window, MONITOR_DEFAULTTOPRIMARY); + pGetDpiForMonitor(hMonitor, (MONITOR_DPI_TYPE)0, &app->dpix, &app->dpiy); + } + } else { + // We couldn't get the window's DPI above, so get the DPI of the primary monitor + // using an API that is available in all Windows versions. + HDC hScreenDC = GetDC(0); + app->dpix = GetDeviceCaps(hScreenDC, LOGPIXELSX); + app->dpiy = GetDeviceCaps(hScreenDC, LOGPIXELSY); + ReleaseDC(0, hScreenDC); + } +} + +struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) { + + // Create application + struct Application *result = (struct Application*)malloc(sizeof(struct Application)); + + result->window = nullptr; + result->webview = nullptr; + result->webviewController = nullptr; + + result->title = title; + result->width = width; + result->height = height; + result->resizable = resizable; + result->devtools = devtools; + result->fullscreen = fullscreen; + result->startHidden = startHidden; + result->logLevel = logLevel; + result->hideWindowOnClose = hideWindowOnClose; + result->webviewIsTranparent = false; + result->WindowIsTranslucent = false; + result->disableWindowIcon = false; + + // Min/Max Width/Height + result->minWidth = 0; + result->minHeight = 0; + result->maxWidth = 0; + result->maxHeight = 0; + + // Default colour + result->backgroundColour.R = 255; + result->backgroundColour.G = 255; + result->backgroundColour.B = 255; + result->backgroundColour.A = 255; + + // Have a frame by default + result->frame = 1; + + // Capture Main Thread + mainThread = GetCurrentThreadId(); + + // Startup url + result->startupURL = nullptr; + + // Used to remember the window location when going fullscreen + result->previousPlacement = { sizeof(result->previousPlacement) }; + + // DPI + result->dpix = result->dpiy = 0; + + return result; +} + +void* GetWindowHandle(struct Application *app) { + return (void*)app->window; +} + +void SetMinWindowSize(struct Application* app, int minWidth, int minHeight) { + app->minWidth = (LONG)minWidth; + app->minHeight = (LONG)minHeight; +} + +void SetMaxWindowSize(struct Application* app, int maxWidth, int maxHeight) { + app->maxWidth = (LONG)maxWidth; + app->maxHeight = (LONG)maxHeight; +} + +void SetBindings(struct Application *app, const char *bindings) { + std::string temp = std::string("window.wailsbindings = \"") + std::string(bindings) + std::string("\";"); + app->bindings = new char[temp.length()+1]; + memcpy(app->bindings, temp.c_str(), temp.length()+1); +} + +void performShutdown(struct Application *app) { + if( app->startupURL != nullptr ) { + delete[] app->startupURL; + } + messageFromWindowCallback("WC"); +} + +// Credit: https://gist.github.com/ysc3839/b08d2bff1c7dacde529bed1d37e85ccf +void enableTranslucentBackground(struct Application *app) { + HMODULE hUser = GetModuleHandleA("user32.dll"); + if (hUser) + { + pfnSetWindowCompositionAttribute setWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute"); + if (setWindowCompositionAttribute) + { + ACCENT_POLICY accent = { ACCENT_ENABLE_BLURBEHIND, 0, 0, 0 }; + WINDOWCOMPOSITIONATTRIBDATA data; + data.Attrib = WCA_ACCENT_POLICY; + data.pvData = &accent; + data.cbData = sizeof(accent); + setWindowCompositionAttribute(app->window, &data); + } + } +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + + struct Application *app = (struct Application *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + + switch(msg) { + + case WM_CREATE: { + createApplicationMenu(hwnd); + break; + } + case WM_COMMAND: + menuClicked(LOWORD(wParam)); + break; + + case WM_CLOSE: { + DestroyWindow( app->window ); + break; + } + case WM_DESTROY: { + if( app->hideWindowOnClose ) { + Hide(app); + } else { + PostQuitMessage(0); + } + break; + } + case WM_SIZE: { + if ( app == NULL ) { + return 0; + } + if( app->webviewController != nullptr) { + RECT bounds; + GetClientRect(app->window, &bounds); + app->webviewController->put_Bounds(bounds); + } + break; + } + case WM_KEYDOWN: + // This is needed because webview2 is sometimes not in focus + // https://github.com/MicrosoftEdge/WebView2Feedback/issues/1541 + processKeyPress(wParam); + break; + case WM_GETMINMAXINFO: { + // Exit early if this is called before the window is created. + if ( app == NULL ) { + return 0; + } + + // update DPI + getDPIForWindow(app); + double DPIScaleX = app->dpix/96.0; + double DPIScaleY = app->dpiy/96.0; + + RECT rcWind; + POINT ptDiff; + GetWindowRect(hwnd, &rcWind); + + int widthExtra = (rcWind.right - rcWind.left); + int heightExtra = (rcWind.bottom - rcWind.top); + + LPMINMAXINFO mmi = (LPMINMAXINFO) lParam; + if (app->minWidth > 0 && app->minHeight > 0) { + mmi->ptMinTrackSize.x = app->minWidth * DPIScaleX; + mmi->ptMinTrackSize.y = app->minHeight * DPIScaleY; + } + if (app->maxWidth > 0 && app->maxHeight > 0) { + mmi->ptMaxSize.x = app->maxWidth * DPIScaleX; + mmi->ptMaxSize.y = app->maxHeight * DPIScaleY; + mmi->ptMaxTrackSize.x = app->maxWidth * DPIScaleX; + mmi->ptMaxTrackSize.y = app->maxHeight * DPIScaleY; + } + return 0; + } + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; +} + +void init(struct Application *app, const char* js) { + LPCWSTR wjs = cstrToLPWSTR(js); + app->webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr); + delete[] wjs; +} + +void execJS(struct Application* app, const char *script) { + LPWSTR s = cstrToLPWSTR(script); + app->webview->ExecuteScript(s, nullptr); + delete[] s; +} + +void loadAssets(struct Application* app) { + + // setup window.wailsInvoke + std::string initialCode = std::string("window.wailsInvoke=function(m){window.chrome.webview.postMessage(m)};"); + + // Load bindings + initialCode += std::string(app->bindings); + delete[] app->bindings; + + // Load runtime + initialCode += std::string((const char*)&runtime); + + int index = 1; + while(1) { + // Get next asset pointer + const unsigned char *asset = assets[index]; + + // If we have no more assets, break + if (asset == 0x00) { + break; + } + + initialCode += std::string((const char*)asset); + index++; + }; + + // Disable context menu if not in debug mode + if( debug != 1 ) { + initialCode += std::string("wails._.DisableDefaultContextMenu();"); + } + + initialCode += std::string("window.wailsInvoke('completed');"); + + // Keep a copy of the code + app->initialCode = new char[initialCode.length()+1]; + memcpy(app->initialCode, initialCode.c_str(), initialCode.length()+1); + + execJS(app, app->initialCode); + + // Show app if we need to + if( app->startHidden == false ) { + Show(app); + } +} + +// This is called when all our assets are loaded into the DOM +void completed(struct Application* app) { + delete[] app->initialCode; + app->initialCode = nullptr; + + // Process whether window should show by default + int startVisibility = SW_SHOWNORMAL; + if ( app->startHidden == 1 ) { + startVisibility = SW_HIDE; + } + + // Fix for webview2 bug: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077 + // Will be fixed in next stable release + app->webviewController->put_IsVisible(false); + app->webviewController->put_IsVisible(true); + + // Private setTitle as we're on the main thread + if( app->frame == 1) { + setTitle(app, app->title); + } + + ShowWindow(app->window, startVisibility); + UpdateWindow(app->window); + SetFocus(app->window); + + if( app->startupURL == nullptr ) { + messageFromWindowCallback("SS"); + return; + } + std::string readyMessage = std::string("SS") + std::string(app->startupURL); + messageFromWindowCallback(readyMessage.c_str()); +} + +// +bool initWebView2(struct Application *app, int debugEnabled, messageCallback cb) { + + debug = debugEnabled; + + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + std::atomic_flag flag = ATOMIC_FLAG_INIT; + flag.test_and_set(); + + char currentExePath[MAX_PATH]; + GetModuleFileNameA(NULL, currentExePath, MAX_PATH); + char *currentExeName = PathFindFileNameA(currentExePath); + + std::wstring_convert> wideCharConverter; + std::wstring userDataFolder = + wideCharConverter.from_bytes(std::getenv("APPDATA")); + std::wstring currentExeNameW = wideCharConverter.from_bytes(currentExeName); + + ICoreWebView2Controller *controller; + ICoreWebView2* webview; + + HRESULT res = CreateCoreWebView2EnvironmentWithOptions( + nullptr, (userDataFolder + L"/" + currentExeNameW).c_str(), nullptr, + new wv2ComHandler(app, app->window, cb, + [&](ICoreWebView2Controller *webviewController) { + controller = webviewController; + controller->get_CoreWebView2(&webview); + webview->AddRef(); + ICoreWebView2Settings* settings; + webview->get_Settings(&settings); + if ( debugEnabled == 0 ) { + settings->put_AreDefaultContextMenusEnabled(FALSE); + } + // Fix for invisible webview + if( app->startHidden ) {} + controller->MoveFocus(COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC); + flag.clear(); + })); + if (!SUCCEEDED(res)) + { + switch (res) + { + case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + { + MessageBox( + app->window, + L"Couldn't find Edge installation. " + "Do you have a version installed that's compatible with this " + "WebView2 SDK version?", + nullptr, MB_OK); + } + break; + case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS): + { + MessageBox( + app->window, L"User data folder cannot be created because a file with the same name already exists.", nullptr, MB_OK); + } + break; + case E_ACCESSDENIED: + { + MessageBox( + app->window, L"Unable to create user data folder, Access Denied.", nullptr, MB_OK); + } + break; + case E_FAIL: + { + MessageBox( + app->window, L"Edge runtime unable to start", nullptr, MB_OK); + } + break; + default: + { + MessageBox(app->window, L"Failed to create WebView2 environment", nullptr, MB_OK); + } + } + } + + if (res != S_OK) { + CoUninitialize(); + return false; + } + + MSG msg = {}; + while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + app->webviewController = controller; + app->webview = webview; + + // Resize WebView to fit the bounds of the parent window + RECT bounds; + GetClientRect(app->window, &bounds); + app->webviewController->put_Bounds(bounds); + + // Let the backend know we have initialised + app->webview->AddScriptToExecuteOnDocumentCreated(L"window.chrome.webview.postMessage('initialised');", nullptr); + // Load the HTML + LPCWSTR html = (LPCWSTR) cstrToLPWSTR((char*)assets[0]); + app->webview->Navigate(html); + + if( app->webviewIsTranparent ) { + wchar_t szBuff[64]; + ICoreWebView2Controller2 *wc2; + wc2 = nullptr; + app->webviewController->QueryInterface(IID_ICoreWebView2Controller2, (void**)&wc2); + + COREWEBVIEW2_COLOR wvColor; + wvColor.R = app->backgroundColour.R; + wvColor.G = app->backgroundColour.G; + wvColor.B = app->backgroundColour.B; + wvColor.A = app->backgroundColour.A == 0 ? 0 : 255; + if( app->WindowIsTranslucent ) { + wvColor.A = 0; + } + HRESULT result = wc2->put_DefaultBackgroundColor(wvColor); + if (!SUCCEEDED(result)) + { + switch (result) + { + case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + { + MessageBox( + app->window, + L"Couldn't find Edge installation. " + "Do you have a version installed that's compatible with this " + "WebView2 SDK version?", + nullptr, MB_OK); + } + break; + case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS): + { + MessageBox( + app->window, L"User data folder cannot be created because a file with the same name already exists.", nullptr, MB_OK); + } + break; + case E_ACCESSDENIED: + { + MessageBox( + app->window, L"Unable to create user data folder, Access Denied.", nullptr, MB_OK); + } + break; + case E_FAIL: + { + MessageBox( + app->window, L"Edge runtime unable to start", nullptr, MB_OK); + } + break; + default: + { + MessageBox(app->window, L"Failed to create WebView2 environment", nullptr, MB_OK); + } + } + } + + } + + messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}"); + + return true; +} + +void initialCallback(std::string message) { + printf("MESSAGE=%s\n", message); +} + +void Run(struct Application* app, int argc, char **argv) { + + // Register the window class. + const wchar_t CLASS_NAME[] = L"Ffenestri"; + + WNDCLASSEX wc = { }; + + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = WndProc; + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = CLASS_NAME; + + if( app->disableWindowIcon == false ) { + wc.hIcon = LoadIcon(wc.hInstance, MAKEINTRESOURCE(100)); + wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(100)); + } + + // Configure translucency + DWORD dwExStyle = 0; + if ( app->WindowIsTranslucent) { + dwExStyle = WS_EX_NOREDIRECTIONBITMAP; + wc.hbrBackground = CreateSolidBrush(RGB(255,255,255)); + } + + RegisterClassEx(&wc); + + // Process window style + DWORD windowStyle = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + + if (app->resizable == 0) { + windowStyle &= ~WS_MAXIMIZEBOX; + windowStyle &= ~WS_THICKFRAME; + } + if ( app->frame == 0 ) { + windowStyle &= ~WS_OVERLAPPEDWINDOW; + windowStyle &= ~WS_CAPTION; + windowStyle |= WS_POPUP; + } + + // Create the window. + app->window = CreateWindowEx( + dwExStyle, // Optional window styles. + CLASS_NAME, // Window class + L"", // Window text + windowStyle, // Window style + + // Size and position + CW_USEDEFAULT, CW_USEDEFAULT, app->width, app->height, + + NULL, // Parent window + NULL, // Menu + wc.hInstance, // Instance handle + NULL // Additional application data + ); + + if (app->window == NULL) + { + return; + } + + if ( app->fullscreen ) { + fullscreen(app); + } + + // Credit: https://stackoverflow.com/a/35482689 + if( app->disableWindowIcon && app->frame == 1 ) { + int extendedStyle = GetWindowLong(app->window, GWL_EXSTYLE); + SetWindowLong(app->window, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME); + SetWindowPos(nullptr, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); + } + + if ( app->WindowIsTranslucent ) { + + // Enable the translucent background effect + enableTranslucentBackground(app); + + // Setup transparency of main window. This allows the blur to show through. + SetLayeredWindowAttributes(app->window,RGB(255,255,255),0,LWA_COLORKEY); + } + + // Store application pointer in window handle + SetWindowLongPtr(app->window, GWLP_USERDATA, (LONG_PTR)app); + + // private center() as we are on main thread + center(app); + + // Add webview2 + initWebView2(app, debug, initialCallback); + + + // Main event loop + MSG msg; + BOOL res; + while ((res = GetMessage(&msg, NULL, 0, 0)) != -1) { + if (msg.hwnd) { + TranslateMessage(&msg); + DispatchMessage(&msg); + continue; + } + if (msg.message == WM_APP) { + dispatchFunction *f = (dispatchFunction*) msg.lParam; + (*f)(); + delete(f); + } else if (msg.message == WM_QUIT) { + performShutdown(app); + return; + } + } +} + +void SetDebug(struct Application* app, int flag) { + debug = flag; +} + +void ExecJS(struct Application* app, const char *script) { + ON_MAIN_THREAD( + execJS(app, script); + ); +} + +void hide(struct Application* app) { + ShowWindow(app->window, SW_HIDE); +} + +void Hide(struct Application* app) { + ON_MAIN_THREAD( + hide(app); + ); +} + +void show(struct Application* app) { + ShowWindow(app->window, SW_SHOW); +} + +void Show(struct Application* app) { + ON_MAIN_THREAD( + show(app); + ); +} + +void DisableWindowIcon(struct Application* app) { + app->disableWindowIcon = true; +} + +void center(struct Application* app) { + + HMONITOR currentMonitor = MonitorFromWindow(app->window, MONITOR_DEFAULTTONEAREST); + MONITORINFO info = {0}; + info.cbSize = sizeof(info); + GetMonitorInfoA(currentMonitor, &info); + RECT workRect = info.rcWork; + LONG screenMiddleW = (workRect.right - workRect.left) / 2; + LONG screenMiddleH = (workRect.bottom - workRect.top) / 2; + RECT winRect; + if (app->frame == 1) { + GetWindowRect(app->window, &winRect); + } else { + GetClientRect(app->window, &winRect); + } + LONG winWidth = winRect.right - winRect.left; + LONG winHeight = winRect.bottom - winRect.top; + + LONG windowX = screenMiddleW - (winWidth / 2); + LONG windowY = screenMiddleH - (winHeight / 2); + + SetWindowPos(app->window, HWND_TOP, windowX, windowY, winWidth, winHeight, SWP_NOSIZE); +} + +void Center(struct Application* app) { + ON_MAIN_THREAD( + center(app); + ); +} + +UINT getWindowPlacement(struct Application* app) { + WINDOWPLACEMENT lpwndpl; + lpwndpl.length = sizeof(WINDOWPLACEMENT); + BOOL result = GetWindowPlacement(app->window, &lpwndpl); + if( result == 0 ) { + // TODO: Work out what this call failing means + return -1; + } + return lpwndpl.showCmd; +} + +int isMaximised(struct Application* app) { + return getWindowPlacement(app) == SW_SHOWMAXIMIZED; +} + +void maximise(struct Application* app) { + ShowWindow(app->window, SW_MAXIMIZE); +} + +void Maximise(struct Application* app) { + ON_MAIN_THREAD( + maximise(app); + ); +} + +void unmaximise(struct Application* app) { + ShowWindow(app->window, SW_RESTORE); +} + +void Unmaximise(struct Application* app) { + ON_MAIN_THREAD( + unmaximise(app); + ); +} + + +void ToggleMaximise(struct Application* app) { + if(isMaximised(app)) { + return Unmaximise(app); + } + return Maximise(app); +} + +int isMinimised(struct Application* app) { + return getWindowPlacement(app) == SW_SHOWMINIMIZED; +} + +void minimise(struct Application* app) { + ShowWindow(app->window, SW_MINIMIZE); +} + +void Minimise(struct Application* app) { + ON_MAIN_THREAD( + minimise(app); + ); +} + +void unminimise(struct Application* app) { + ShowWindow(app->window, SW_RESTORE); +} + +void Unminimise(struct Application* app) { + ON_MAIN_THREAD( + unminimise(app); + ); +} + +void ToggleMinimise(struct Application* app) { + if(isMinimised(app)) { + return Unminimise(app); + } + return Minimise(app); +} + +void SetColour(struct Application* app, int red, int green, int blue, int alpha) { + app->backgroundColour.R = red; + app->backgroundColour.G = green; + app->backgroundColour.B = blue; + app->backgroundColour.A = alpha; +} + +void SetSize(struct Application* app, int width, int height) { + if( app->maxWidth > 0 && width > app->maxWidth ) { + width = app->maxWidth; + } + if ( app->maxHeight > 0 && height > app->maxHeight ) { + height = app->maxHeight; + } + SetWindowPos(app->window, nullptr, 0, 0, width, height, SWP_NOMOVE); +} + +void setPosition(struct Application* app, int x, int y) { + HMONITOR currentMonitor = MonitorFromWindow(app->window, MONITOR_DEFAULTTONEAREST); + MONITORINFO info = {0}; + info.cbSize = sizeof(info); + GetMonitorInfoA(currentMonitor, &info); + RECT workRect = info.rcWork; + LONG newX = workRect.left + x; + LONG newY = workRect.top + y; + + SetWindowPos(app->window, HWND_TOP, newX, newY, 0, 0, SWP_NOSIZE); +} + +void SetPosition(struct Application* app, int x, int y) { + ON_MAIN_THREAD( + setPosition(app, x, y); + ); +} + +void Quit(struct Application* app) { + // Override the hide window on close flag + app->hideWindowOnClose = 0; + ON_MAIN_THREAD( + DestroyWindow(app->window); + ); +} + + +// Credit: https://stackoverflow.com/a/6693107 +void setTitle(struct Application* app, const char *title) { + LPCTSTR text = cstrToLPWSTR(title); + SetWindowText(app->window, text); + delete[] text; +} + +void SetTitle(struct Application* app, const char *title) { + ON_MAIN_THREAD( + setTitle(app, title); + ); +} + +void fullscreen(struct Application* app) { + + // Ensure we aren't in fullscreen + if (app->isFullscreen) return; + + app->isFullscreen = true; + app->previousWindowStyle = GetWindowLong(app->window, GWL_STYLE); + MONITORINFO mi = { sizeof(mi) }; + if (GetWindowPlacement(app->window, &(app->previousPlacement)) && GetMonitorInfo(MonitorFromWindow(app->window, MONITOR_DEFAULTTOPRIMARY), &mi)) { + SetWindowLong(app->window, GWL_STYLE, app->previousWindowStyle & ~WS_OVERLAPPEDWINDOW); + SetWindowPos(app->window, HWND_TOP, + mi.rcMonitor.left, + mi.rcMonitor.top, + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + } +} + +void Fullscreen(struct Application* app) { + ON_MAIN_THREAD( + fullscreen(app); + show(app); + ); +} + +void unfullscreen(struct Application* app) { + if (app->isFullscreen) { + SetWindowLong(app->window, GWL_STYLE, app->previousWindowStyle); + SetWindowPlacement(app->window, &(app->previousPlacement)); + SetWindowPos(app->window, NULL, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + app->isFullscreen = false; + } +} + +void UnFullscreen(struct Application* app) { + ON_MAIN_THREAD( + unfullscreen(app); + ); +} + +void DisableFrame(struct Application* app) { + app->frame = 0; +} + +// WebviewIsTransparent will make the webview transparent +// revealing the window underneath +void WebviewIsTransparent(struct Application *app) { + app->webviewIsTranparent = true; +} + +void WindowIsTranslucent(struct Application *app) { + app->WindowIsTranslucent = true; +} + + +void OpenDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) { +} +void SaveDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) { +} +void MessageDialog(struct Application* app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) { +} +void DarkModeEnabled(struct Application* app, char *callbackID) { +} +void SetApplicationMenu(struct Application* app, const char *applicationMenuJSON) { +} +void AddTrayMenu(struct Application* app, const char *menuTrayJSON) { +} +void SetTrayMenu(struct Application* app, const char *menuTrayJSON) { +} +void DeleteTrayMenuByID(struct Application* app, const char *id) { +} +void UpdateTrayMenuLabel(struct Application* app, const char* JSON) { +} +void AddContextMenu(struct Application* app, char *contextMenuJSON) { +} +void UpdateContextMenu(struct Application* app, char *contextMenuJSON) { +} \ No newline at end of file diff --git a/v2/internal/ffenestri/ffenestri_windows.go b/v2/internal/ffenestri/ffenestri_windows.go new file mode 100644 index 000000000..ce5d821f3 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_windows.go @@ -0,0 +1,198 @@ +package ffenestri + +import "C" + +/* + +#cgo windows CXXFLAGS: -std=c++11 +#cgo windows,amd64 LDFLAGS: -lgdi32 -lole32 -lShlwapi -luser32 -loleaut32 -ldwmapi + +#include "ffenestri.h" + +extern void DisableWindowIcon(struct Application* app); + +*/ +import "C" +import ( + "github.com/ztrue/tracerr" + "os" + + "github.com/wailsapp/wails/v2/pkg/menu" +) + +// Setup the global caches +var globalCheckboxCache = NewCheckboxCache() +var globalRadioGroupCache = NewRadioGroupCache() +var globalRadioGroupMap = NewRadioGroupMap() +var globalApplicationMenu *Menu + +type menuType string + +const ( + appMenuType menuType = "ApplicationMenu" + contextMenuType + trayMenuType +) + +func (a *Application) processPlatformSettings() error { + + menuManager = a.menuManager + config := a.config.Windows + if config == nil { + return nil + } + + // Check if the webview should be transparent + if config.WebviewIsTransparent { + C.WebviewIsTransparent(a.app) + } + + if config.WindowIsTranslucent { + C.WindowIsTranslucent(a.app) + } + + if config.DisableWindowIcon { + C.DisableWindowIcon(a.app) + } + + // Unfortunately, we need to store this in the package variable so the C callback can see it + applicationMenu = a.menuManager.GetProcessedApplicationMenu() + + // + //// Process tray + //trays, err := a.menuManager.GetTrayMenus() + //if err != nil { + // return err + //} + //if trays != nil { + // for _, tray := range trays { + // C.AddTrayMenu(a.app, a.string2CString(tray)) + // } + //} + // + //// Process context menus + //contextMenus, err := a.menuManager.GetContextMenus() + //if err != nil { + // return err + //} + //if contextMenus != nil { + // for _, contextMenu := range contextMenus { + // C.AddContextMenu(a.app, a.string2CString(contextMenu)) + // } + //} + // + //// Process URL Handlers + //if a.config.Mac.URLHandlers != nil { + // C.HasURLHandlers(a.app) + //} + + return nil +} + +func (c *Client) updateApplicationMenu() { + applicationMenu = c.app.menuManager.GetProcessedApplicationMenu() + createApplicationMenu(uintptr(C.GetWindowHandle(c.app.app))) +} + +/* --------------------------------------------------------------------------------- + +Application Menu +---------------- +There's only 1 application menu and this is where we create it. This method +is called from C after the window is created and the WM_CREATE message has +been sent. + +*/ + +func checkFatal(err error) { + if err != nil { + tracerr.PrintSourceColor(err) + globalRadioGroupCache.Dump() + globalRadioGroupMap.Dump() + os.Exit(1) + } +} + +//export createApplicationMenu +func createApplicationMenu(hwnd uintptr) { + + if applicationMenu == nil { + return + } + + var err error + window := win32Window(hwnd) + + if globalApplicationMenu != nil { + checkFatal(globalApplicationMenu.Destroy()) + } + + globalApplicationMenu, err = createMenu(applicationMenu, appMenuType) + checkFatal(err) + + err = setWindowMenu(window, globalApplicationMenu.menu) + checkFatal(err) +} + +//export handleKeypressInGo +func handleKeypressInGo(keycode uint16, modifiers uint8) { + //fmt.Printf("Key code: %#x\n", keycode) + menuID, menuType := getCallbackForKeyPress(keycode, modifiers) + if menuID == "" { + return + } + err := menuManager.ProcessClick(menuID, "", string(menuType), "") + if err != nil { + println(err.Error()) + } +} + +/* +This method is called by C when a menu item is pressed +*/ + +//export menuClicked +func menuClicked(id uint32) { + win32MenuID := win32MenuItemID(id) + //println("Got click from menu id", win32MenuID) + + // Get the menu from the cache + menuItemDetails := getMenuCacheEntry(win32MenuID) + wailsMenuID := wailsMenuItemID(menuItemDetails.item.ID) + + //println("Got click from menu id", win32MenuID, "- wails menu ID", wailsMenuID) + //spew.Dump(menuItemDetails) + + switch menuItemDetails.item.Type { + case menu.CheckboxType: + + // Determine if the menu is set or not + res, _, err := win32GetMenuState.Call(uintptr(menuItemDetails.parent), uintptr(id), uintptr(MF_BYCOMMAND)) + if int(res) == -1 { + checkFatal(err) + } + + flag := MF_CHECKED + if uint32(res) == MF_CHECKED { + flag = MF_UNCHECKED + } + + for _, menuid := range globalCheckboxCache.win32MenuIDsForWailsMenuID(wailsMenuID) { + //println("setting menuid", menuid, "with flag", flag) + menuItemDetails := getMenuCacheEntry(menuid) + res, _, err = win32CheckMenuItem.Call(uintptr(menuItemDetails.parent), uintptr(menuid), uintptr(flag)) + if int(res) == -1 { + checkFatal(err) + } + } + case menu.RadioType: + err := selectRadioItemFromWailsMenuID(wailsMenuID, win32MenuID) + checkFatal(err) + } + + // Print the click error - it's not fatal + err := menuManager.ProcessClick(menuItemDetails.item.ID, "", string(menuItemDetails.menuType), "") + if err != nil { + println(err.Error()) + } +} diff --git a/v2/internal/ffenestri/ffenestri_windows.h b/v2/internal/ffenestri/ffenestri_windows.h new file mode 100644 index 000000000..1e39bea10 --- /dev/null +++ b/v2/internal/ffenestri/ffenestri_windows.h @@ -0,0 +1,95 @@ + +#ifndef _FFENESTRI_WINDOWS_H +#define _FFENESTRI_WINDOWS_H + +#define WIN32_LEAN_AND_MEAN +#define UNICODE 1 + +#include "ffenestri.h" +#include +#include +#include +#include +#include "windows/WebView2.h" + +#include "assets.h" + +// TODO: +//#include "userdialogicons.h" + + +struct Application{ + // Window specific + HWND window; + ICoreWebView2 *webview; + ICoreWebView2Controller* webviewController; + + // Application + const char *title; + int width; + int height; + int resizable; + int devtools; + int fullscreen; + int startHidden; + int logLevel; + int hideWindowOnClose; + int minSizeSet; + LONG minWidth; + LONG minHeight; + int maxSizeSet; + LONG maxWidth; + LONG maxHeight; + int frame; + char *startupURL; + bool webviewIsTranparent; + bool WindowIsTranslucent; + COREWEBVIEW2_COLOR backgroundColour; + bool disableWindowIcon; + + // Used by fullscreen/unfullscreen + bool isFullscreen; + WINDOWPLACEMENT previousPlacement; + DWORD previousWindowStyle; + + // placeholders + char* bindings; + char* initialCode; + + // DPI + UINT dpix; + UINT dpiy; +}; + +#define ON_MAIN_THREAD(code) dispatch( [=]{ code; } ) + +typedef std::function dispatchFunction; +typedef std::function messageCallback; +typedef std::function comHandlerCallback; + +void center(struct Application*); +void setTitle(struct Application* app, const char *title); +void fullscreen(struct Application* app); +void unfullscreen(struct Application* app); +char* LPWSTRToCstr(LPWSTR input); + +// called when the DOM is ready +void loadAssets(struct Application* app); + +// called when the application assets have been loaded into the DOM +void completed(struct Application* app); + +// Processes the given keycode +void processKeyPress(UINT key); + +// Callback +extern "C" { + void DisableWindowIcon(struct Application* app); + void messageFromWindowCallback(const char *); + void* GetWindowHandle(struct Application*); + void createApplicationMenu(HWND hwnd); + void menuClicked(UINT id); + void handleKeypressInGo(UINT, BYTE); +} + +#endif \ No newline at end of file diff --git a/v2/internal/ffenestri/hashmap.h b/v2/internal/ffenestri/hashmap.h new file mode 100644 index 000000000..0278bc3d6 --- /dev/null +++ b/v2/internal/ffenestri/hashmap.h @@ -0,0 +1,518 @@ +/* + The latest version of this library is available on GitHub; + https://github.com/sheredom/hashmap.h +*/ + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +#ifndef SHEREDOM_HASHMAP_H_INCLUDED +#define SHEREDOM_HASHMAP_H_INCLUDED + +#if defined(_MSC_VER) +// Workaround a bug in the MSVC runtime where it uses __cplusplus when not +// defined. +#pragma warning(push, 0) +#pragma warning(disable : 4668) +#endif +#include +#include + +#if (defined(_MSC_VER) && defined(__AVX__)) || \ + (!defined(_MSC_VER) && defined(__SSE4_2__)) +#define HASHMAP_SSE42 +#endif + +#if defined(HASHMAP_SSE42) +#include +#endif + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(_MSC_VER) +#pragma warning(push) +// Stop MSVC complaining about not inlining functions. +#pragma warning(disable : 4710) +// Stop MSVC complaining about inlining functions! +#pragma warning(disable : 4711) +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif + +#if defined(_MSC_VER) +#define HASHMAP_USED +#elif defined(__GNUC__) +#define HASHMAP_USED __attribute__((used)) +#else +#define HASHMAP_USED +#endif + +/* We need to keep keys and values. */ +struct hashmap_element_s { + const char *key; + unsigned key_len; + int in_use; + void *data; +}; + +/* A hashmap has some maximum size and current size, as well as the data to + * hold. */ +struct hashmap_s { + unsigned table_size; + unsigned size; + struct hashmap_element_s *data; +}; + +#define HASHMAP_MAX_CHAIN_LENGTH (8) + +#if defined(__cplusplus) +extern "C" { +#endif + +/// @brief Create a hashmap. +/// @param initial_size The initial size of the hashmap. Must be a power of two. +/// @param out_hashmap The storage for the created hashmap. +/// @return On success 0 is returned. +/// +/// Note that the initial size of the hashmap must be a power of two, and +/// creation of the hashmap will fail if this is not the case. +static int hashmap_create(const unsigned initial_size, + struct hashmap_s *const out_hashmap) HASHMAP_USED; + +/// @brief Put an element into the hashmap. +/// @param hashmap The hashmap to insert into. +/// @param key The string key to use. +/// @param len The length of the string key. +/// @param value The value to insert. +/// @return On success 0 is returned. +/// +/// The key string slice is not copied when creating the hashmap entry, and thus +/// must remain a valid pointer until the hashmap entry is removed or the +/// hashmap is destroyed. +static int hashmap_put(struct hashmap_s *const hashmap, const char *const key, + const unsigned len, void *const value) HASHMAP_USED; + +/// @brief Get an element from the hashmap. +/// @param hashmap The hashmap to get from. +/// @param key The string key to use. +/// @param len The length of the string key. +/// @return The previously set element, or NULL if none exists. +static void *hashmap_get(const struct hashmap_s *const hashmap, + const char *const key, + const unsigned len) HASHMAP_USED; + +/// @brief Remove an element from the hashmap. +/// @param hashmap The hashmap to remove from. +/// @param key The string key to use. +/// @param len The length of the string key. +/// @return On success 0 is returned. +static int hashmap_remove(struct hashmap_s *const hashmap, + const char *const key, + const unsigned len) HASHMAP_USED; + +/// @brief Iterate over all the elements in a hashmap. +/// @param hashmap The hashmap to iterate over. +/// @param f The function pointer to call on each element. +/// @param context The context to pass as the first argument to f. +/// @return If the entire hashmap was iterated then 0 is returned. Otherwise if +/// the callback function f returned non-zero then non-zero is returned. +static int hashmap_iterate(const struct hashmap_s *const hashmap, + int (*f)(void *const context, void *const value), + void *const context) HASHMAP_USED; + +/// @brief Iterate over all the elements in a hashmap. +/// @param hashmap The hashmap to iterate over. +/// @param f The function pointer to call on each element. +/// @param context The context to pass as the first argument to f. +/// @return If the entire hashmap was iterated then 0 is returned. +/// Otherwise if the callback function f returned positive then the positive +/// value is returned. If the callback function returns -1, the current item +/// is removed and iteration continues. +static int hashmap_iterate_pairs(struct hashmap_s *const hashmap, + int (*f)(void *const, struct hashmap_element_s *const), + void *const context) HASHMAP_USED; + +/// @brief Get the size of the hashmap. +/// @param hashmap The hashmap to get the size of. +/// @return The size of the hashmap. +static unsigned +hashmap_num_entries(const struct hashmap_s *const hashmap) HASHMAP_USED; + +/// @brief Destroy the hashmap. +/// @param hashmap The hashmap to destroy. +static void hashmap_destroy(struct hashmap_s *const hashmap) HASHMAP_USED; + +static unsigned hashmap_crc32_helper(const char *const s, + const unsigned len) HASHMAP_USED; +static unsigned +hashmap_hash_helper_int_helper(const struct hashmap_s *const m, + const char *const keystring, + const unsigned len) HASHMAP_USED; +static int hashmap_match_helper(const struct hashmap_element_s *const element, + const char *const key, + const unsigned len) HASHMAP_USED; +static int hashmap_hash_helper(const struct hashmap_s *const m, + const char *const key, const unsigned len, + unsigned *const out_index) HASHMAP_USED; +static int hashmap_rehash_iterator(void *const new_hash, + struct hashmap_element_s *const e) HASHMAP_USED; +static int hashmap_rehash_helper(struct hashmap_s *const m) HASHMAP_USED; + +#if defined(__cplusplus) +} +#endif + +#if defined(__cplusplus) +#define HASHMAP_CAST(type, x) static_cast(x) +#define HASHMAP_PTR_CAST(type, x) reinterpret_cast(x) +#define HASHMAP_NULL NULL +#else +#define HASHMAP_CAST(type, x) ((type)x) +#define HASHMAP_PTR_CAST(type, x) ((type)x) +#define HASHMAP_NULL 0 +#endif + +int hashmap_create(const unsigned initial_size, + struct hashmap_s *const out_hashmap) { + if (0 == initial_size || 0 != (initial_size & (initial_size - 1))) { + return 1; + } + + out_hashmap->data = + HASHMAP_CAST(struct hashmap_element_s *, + calloc(initial_size, sizeof(struct hashmap_element_s))); + if (!out_hashmap->data) { + return 1; + } + + out_hashmap->table_size = initial_size; + out_hashmap->size = 0; + + return 0; +} + +int hashmap_put(struct hashmap_s *const m, const char *const key, + const unsigned len, void *const value) { + unsigned int index; + + /* Find a place to put our value. */ + while (!hashmap_hash_helper(m, key, len, &index)) { + if (hashmap_rehash_helper(m)) { + return 1; + } + } + + /* Set the data. */ + m->data[index].data = value; + m->data[index].key = key; + m->data[index].key_len = len; + m->data[index].in_use = 1; + m->size++; + + return 0; +} + +void *hashmap_get(const struct hashmap_s *const m, const char *const key, + const unsigned len) { + unsigned int curr; + unsigned int i; + + /* Find data location */ + curr = hashmap_hash_helper_int_helper(m, key, len); + + /* Linear probing, if necessary */ + for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) { + if (m->data[curr].in_use) { + if (hashmap_match_helper(&m->data[curr], key, len)) { + return m->data[curr].data; + } + } + + curr = (curr + 1) % m->table_size; + } + + /* Not found */ + return HASHMAP_NULL; +} + +int hashmap_remove(struct hashmap_s *const m, const char *const key, + const unsigned len) { + unsigned int i; + unsigned int curr; + + /* Find key */ + curr = hashmap_hash_helper_int_helper(m, key, len); + + /* Linear probing, if necessary */ + for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) { + if (m->data[curr].in_use) { + if (hashmap_match_helper(&m->data[curr], key, len)) { + /* Blank out the fields including in_use */ + memset(&m->data[curr], 0, sizeof(struct hashmap_element_s)); + + /* Reduce the size */ + m->size--; + return 0; + } + } + curr = (curr + 1) % m->table_size; + } + + return 1; +} + +int hashmap_iterate(const struct hashmap_s *const m, + int (*f)(void *const, void *const), void *const context) { + unsigned int i; + + /* Linear probing */ + for (i = 0; i < m->table_size; i++) { + if (m->data[i].in_use) { + if (!f(context, m->data[i].data)) { + return 1; + } + } + } + return 0; +} + +int hashmap_iterate_pairs(struct hashmap_s *const hashmap, + int (*f)(void *const, struct hashmap_element_s *const), + void *const context) { + unsigned int i; + struct hashmap_element_s *p; + int r; + + /* Linear probing */ + for (i = 0; i < hashmap->table_size; i++) { + p=&hashmap->data[i]; + if (p->in_use) { + r=f(context, p); + switch (r) + { + case -1: /* remove item */ + memset(p, 0, sizeof(struct hashmap_element_s)); + hashmap->size--; + break; + case 0: /* continue iterating */ + break; + default: /* early exit */ + return 1; + } + } + } + return 0; +} + +void hashmap_destroy(struct hashmap_s *const m) { + free(m->data); + memset(m, 0, sizeof(struct hashmap_s)); +} + +unsigned hashmap_num_entries(const struct hashmap_s *const m) { + return m->size; +} + +unsigned hashmap_crc32_helper(const char *const s, const unsigned len) { + unsigned i; + unsigned crc32val = 0; + +#if defined(HASHMAP_SSE42) + for (i = 0; i < len; i++) { + crc32val = _mm_crc32_u8(crc32val, HASHMAP_CAST(unsigned char, s[i])); + } + + return crc32val; +#else + // Using polynomial 0x11EDC6F41 to match SSE 4.2's crc function. + static const unsigned crc32_tab[] = { + 0x00000000U, 0xF26B8303U, 0xE13B70F7U, 0x1350F3F4U, 0xC79A971FU, + 0x35F1141CU, 0x26A1E7E8U, 0xD4CA64EBU, 0x8AD958CFU, 0x78B2DBCCU, + 0x6BE22838U, 0x9989AB3BU, 0x4D43CFD0U, 0xBF284CD3U, 0xAC78BF27U, + 0x5E133C24U, 0x105EC76FU, 0xE235446CU, 0xF165B798U, 0x030E349BU, + 0xD7C45070U, 0x25AFD373U, 0x36FF2087U, 0xC494A384U, 0x9A879FA0U, + 0x68EC1CA3U, 0x7BBCEF57U, 0x89D76C54U, 0x5D1D08BFU, 0xAF768BBCU, + 0xBC267848U, 0x4E4DFB4BU, 0x20BD8EDEU, 0xD2D60DDDU, 0xC186FE29U, + 0x33ED7D2AU, 0xE72719C1U, 0x154C9AC2U, 0x061C6936U, 0xF477EA35U, + 0xAA64D611U, 0x580F5512U, 0x4B5FA6E6U, 0xB93425E5U, 0x6DFE410EU, + 0x9F95C20DU, 0x8CC531F9U, 0x7EAEB2FAU, 0x30E349B1U, 0xC288CAB2U, + 0xD1D83946U, 0x23B3BA45U, 0xF779DEAEU, 0x05125DADU, 0x1642AE59U, + 0xE4292D5AU, 0xBA3A117EU, 0x4851927DU, 0x5B016189U, 0xA96AE28AU, + 0x7DA08661U, 0x8FCB0562U, 0x9C9BF696U, 0x6EF07595U, 0x417B1DBCU, + 0xB3109EBFU, 0xA0406D4BU, 0x522BEE48U, 0x86E18AA3U, 0x748A09A0U, + 0x67DAFA54U, 0x95B17957U, 0xCBA24573U, 0x39C9C670U, 0x2A993584U, + 0xD8F2B687U, 0x0C38D26CU, 0xFE53516FU, 0xED03A29BU, 0x1F682198U, + 0x5125DAD3U, 0xA34E59D0U, 0xB01EAA24U, 0x42752927U, 0x96BF4DCCU, + 0x64D4CECFU, 0x77843D3BU, 0x85EFBE38U, 0xDBFC821CU, 0x2997011FU, + 0x3AC7F2EBU, 0xC8AC71E8U, 0x1C661503U, 0xEE0D9600U, 0xFD5D65F4U, + 0x0F36E6F7U, 0x61C69362U, 0x93AD1061U, 0x80FDE395U, 0x72966096U, + 0xA65C047DU, 0x5437877EU, 0x4767748AU, 0xB50CF789U, 0xEB1FCBADU, + 0x197448AEU, 0x0A24BB5AU, 0xF84F3859U, 0x2C855CB2U, 0xDEEEDFB1U, + 0xCDBE2C45U, 0x3FD5AF46U, 0x7198540DU, 0x83F3D70EU, 0x90A324FAU, + 0x62C8A7F9U, 0xB602C312U, 0x44694011U, 0x5739B3E5U, 0xA55230E6U, + 0xFB410CC2U, 0x092A8FC1U, 0x1A7A7C35U, 0xE811FF36U, 0x3CDB9BDDU, + 0xCEB018DEU, 0xDDE0EB2AU, 0x2F8B6829U, 0x82F63B78U, 0x709DB87BU, + 0x63CD4B8FU, 0x91A6C88CU, 0x456CAC67U, 0xB7072F64U, 0xA457DC90U, + 0x563C5F93U, 0x082F63B7U, 0xFA44E0B4U, 0xE9141340U, 0x1B7F9043U, + 0xCFB5F4A8U, 0x3DDE77ABU, 0x2E8E845FU, 0xDCE5075CU, 0x92A8FC17U, + 0x60C37F14U, 0x73938CE0U, 0x81F80FE3U, 0x55326B08U, 0xA759E80BU, + 0xB4091BFFU, 0x466298FCU, 0x1871A4D8U, 0xEA1A27DBU, 0xF94AD42FU, + 0x0B21572CU, 0xDFEB33C7U, 0x2D80B0C4U, 0x3ED04330U, 0xCCBBC033U, + 0xA24BB5A6U, 0x502036A5U, 0x4370C551U, 0xB11B4652U, 0x65D122B9U, + 0x97BAA1BAU, 0x84EA524EU, 0x7681D14DU, 0x2892ED69U, 0xDAF96E6AU, + 0xC9A99D9EU, 0x3BC21E9DU, 0xEF087A76U, 0x1D63F975U, 0x0E330A81U, + 0xFC588982U, 0xB21572C9U, 0x407EF1CAU, 0x532E023EU, 0xA145813DU, + 0x758FE5D6U, 0x87E466D5U, 0x94B49521U, 0x66DF1622U, 0x38CC2A06U, + 0xCAA7A905U, 0xD9F75AF1U, 0x2B9CD9F2U, 0xFF56BD19U, 0x0D3D3E1AU, + 0x1E6DCDEEU, 0xEC064EEDU, 0xC38D26C4U, 0x31E6A5C7U, 0x22B65633U, + 0xD0DDD530U, 0x0417B1DBU, 0xF67C32D8U, 0xE52CC12CU, 0x1747422FU, + 0x49547E0BU, 0xBB3FFD08U, 0xA86F0EFCU, 0x5A048DFFU, 0x8ECEE914U, + 0x7CA56A17U, 0x6FF599E3U, 0x9D9E1AE0U, 0xD3D3E1ABU, 0x21B862A8U, + 0x32E8915CU, 0xC083125FU, 0x144976B4U, 0xE622F5B7U, 0xF5720643U, + 0x07198540U, 0x590AB964U, 0xAB613A67U, 0xB831C993U, 0x4A5A4A90U, + 0x9E902E7BU, 0x6CFBAD78U, 0x7FAB5E8CU, 0x8DC0DD8FU, 0xE330A81AU, + 0x115B2B19U, 0x020BD8EDU, 0xF0605BEEU, 0x24AA3F05U, 0xD6C1BC06U, + 0xC5914FF2U, 0x37FACCF1U, 0x69E9F0D5U, 0x9B8273D6U, 0x88D28022U, + 0x7AB90321U, 0xAE7367CAU, 0x5C18E4C9U, 0x4F48173DU, 0xBD23943EU, + 0xF36E6F75U, 0x0105EC76U, 0x12551F82U, 0xE03E9C81U, 0x34F4F86AU, + 0xC69F7B69U, 0xD5CF889DU, 0x27A40B9EU, 0x79B737BAU, 0x8BDCB4B9U, + 0x988C474DU, 0x6AE7C44EU, 0xBE2DA0A5U, 0x4C4623A6U, 0x5F16D052U, + 0xAD7D5351U}; + + for (i = 0; i < len; i++) { + crc32val = crc32_tab[(HASHMAP_CAST(unsigned char, crc32val) ^ + HASHMAP_CAST(unsigned char, s[i]))] ^ + (crc32val >> 8); + } + return crc32val; +#endif +} + +unsigned hashmap_hash_helper_int_helper(const struct hashmap_s *const m, + const char *const keystring, + const unsigned len) { + unsigned key = hashmap_crc32_helper(keystring, len); + + /* Robert Jenkins' 32 bit Mix Function */ + key += (key << 12); + key ^= (key >> 22); + key += (key << 4); + key ^= (key >> 9); + key += (key << 10); + key ^= (key >> 2); + key += (key << 7); + key ^= (key >> 12); + + /* Knuth's Multiplicative Method */ + key = (key >> 3) * 2654435761; + + return key % m->table_size; +} + +int hashmap_match_helper(const struct hashmap_element_s *const element, + const char *const key, const unsigned len) { + return (element->key_len == len) && (0 == memcmp(element->key, key, len)); +} + +int hashmap_hash_helper(const struct hashmap_s *const m, const char *const key, + const unsigned len, unsigned *const out_index) { + unsigned int curr; + unsigned int i; + + /* If full, return immediately */ + if (m->size >= m->table_size) { + return 0; + } + + /* Find the best index */ + curr = hashmap_hash_helper_int_helper(m, key, len); + + /* Linear probing */ + for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) { + if (!m->data[curr].in_use) { + *out_index = curr; + return 1; + } + + if (m->data[curr].in_use && + hashmap_match_helper(&m->data[curr], key, len)) { + *out_index = curr; + return 1; + } + + curr = (curr + 1) % m->table_size; + } + + return 0; +} + +int hashmap_rehash_iterator(void *const new_hash, + struct hashmap_element_s *const e) { + int temp=hashmap_put(HASHMAP_PTR_CAST(struct hashmap_s *, new_hash), + e->key, e->key_len, e->data); + if (0table_size; + + struct hashmap_s new_hash; + + int flag = hashmap_create(new_size, &new_hash); + if (0!=flag) { + return flag; + } + + /* copy the old elements to the new table */ + flag = hashmap_iterate_pairs(m, hashmap_rehash_iterator, HASHMAP_PTR_CAST(void *, &new_hash)); + if (0!=flag) { + return flag; + } + + hashmap_destroy(m); + /* put new hash into old hash structure by copying */ + memcpy(m, &new_hash, sizeof(struct hashmap_s)); + + return 0; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#elif defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif \ No newline at end of file diff --git a/v2/internal/ffenestri/json.c b/v2/internal/ffenestri/json.c new file mode 100644 index 000000000..d62ff9a03 --- /dev/null +++ b/v2/internal/ffenestri/json.c @@ -0,0 +1,1403 @@ +// +build !windows + +/* + Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Source: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD +*/ + +#include "json.h" + +#include +#include +#include +#include +#include + +#define out_of_memory() do { \ + fprintf(stderr, "Out of memory.\n"); \ + exit(EXIT_FAILURE); \ + } while (0) + +/* Sadly, strdup is not portable. */ +static char *json_strdup(const char *str) +{ + char *ret = (char*) malloc(strlen(str) + 1); + if (ret == NULL) + out_of_memory(); + strcpy(ret, str); + return ret; +} + +/* String buffer */ + +typedef struct +{ + char *cur; + char *end; + char *start; +} SB; + +static void sb_init(SB *sb) +{ + sb->start = (char*) malloc(17); + if (sb->start == NULL) + out_of_memory(); + sb->cur = sb->start; + sb->end = sb->start + 16; +} + +/* sb and need may be evaluated multiple times. */ +#define sb_need(sb, need) do { \ + if ((sb)->end - (sb)->cur < (need)) \ + sb_grow(sb, need); \ + } while (0) + +static void sb_grow(SB *sb, int need) +{ + size_t length = sb->cur - sb->start; + size_t alloc = sb->end - sb->start; + + do { + alloc *= 2; + } while (alloc < length + need); + + sb->start = (char*) realloc(sb->start, alloc + 1); + if (sb->start == NULL) + out_of_memory(); + sb->cur = sb->start + length; + sb->end = sb->start + alloc; +} + +static void sb_put(SB *sb, const char *bytes, int count) +{ + sb_need(sb, count); + memcpy(sb->cur, bytes, count); + sb->cur += count; +} + +#define sb_putc(sb, c) do { \ + if ((sb)->cur >= (sb)->end) \ + sb_grow(sb, 1); \ + *(sb)->cur++ = (c); \ + } while (0) + +static void sb_puts(SB *sb, const char *str) +{ + sb_put(sb, str, strlen(str)); +} + +static char *sb_finish(SB *sb) +{ + *sb->cur = 0; + assert(sb->start <= sb->cur && strlen(sb->start) == (size_t)(sb->cur - sb->start)); + return sb->start; +} + +static void sb_free(SB *sb) +{ + free(sb->start); +} + +/* + * Unicode helper functions + * + * These are taken from the ccan/charset module and customized a bit. + * Putting them here means the compiler can (choose to) inline them, + * and it keeps ccan/json from having a dependency. + */ + +/* + * Type for Unicode codepoints. + * We need our own because wchar_t might be 16 bits. + */ +typedef uint32_t uchar_t; + +/* + * Validate a single UTF-8 character starting at @s. + * The string must be null-terminated. + * + * If it's valid, return its length (1 thru 4). + * If it's invalid or clipped, return 0. + * + * This function implements the syntax given in RFC3629, which is + * the same as that given in The Unicode Standard, Version 6.0. + * + * It has the following properties: + * + * * All codepoints U+0000..U+10FFFF may be encoded, + * except for U+D800..U+DFFF, which are reserved + * for UTF-16 surrogate pair encoding. + * * UTF-8 byte sequences longer than 4 bytes are not permitted, + * as they exceed the range of Unicode. + * * The sixty-six Unicode "non-characters" are permitted + * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). + */ +static int utf8_validate_cz(const char *s) +{ + unsigned char c = *s++; + + if (c <= 0x7F) { /* 00..7F */ + return 1; + } else if (c <= 0xC1) { /* 80..C1 */ + /* Disallow overlong 2-byte sequence. */ + return 0; + } else if (c <= 0xDF) { /* C2..DF */ + /* Make sure subsequent byte is in the range 0x80..0xBF. */ + if (((unsigned char)*s++ & 0xC0) != 0x80) + return 0; + + return 2; + } else if (c <= 0xEF) { /* E0..EF */ + /* Disallow overlong 3-byte sequence. */ + if (c == 0xE0 && (unsigned char)*s < 0xA0) + return 0; + + /* Disallow U+D800..U+DFFF. */ + if (c == 0xED && (unsigned char)*s > 0x9F) + return 0; + + /* Make sure subsequent bytes are in the range 0x80..0xBF. */ + if (((unsigned char)*s++ & 0xC0) != 0x80) + return 0; + if (((unsigned char)*s++ & 0xC0) != 0x80) + return 0; + + return 3; + } else if (c <= 0xF4) { /* F0..F4 */ + /* Disallow overlong 4-byte sequence. */ + if (c == 0xF0 && (unsigned char)*s < 0x90) + return 0; + + /* Disallow codepoints beyond U+10FFFF. */ + if (c == 0xF4 && (unsigned char)*s > 0x8F) + return 0; + + /* Make sure subsequent bytes are in the range 0x80..0xBF. */ + if (((unsigned char)*s++ & 0xC0) != 0x80) + return 0; + if (((unsigned char)*s++ & 0xC0) != 0x80) + return 0; + if (((unsigned char)*s++ & 0xC0) != 0x80) + return 0; + + return 4; + } else { /* F5..FF */ + return 0; + } +} + +/* Validate a null-terminated UTF-8 string. */ +static bool utf8_validate(const char *s) +{ + int len; + + for (; *s != 0; s += len) { + len = utf8_validate_cz(s); + if (len == 0) + return false; + } + + return true; +} + +/* + * Read a single UTF-8 character starting at @s, + * returning the length, in bytes, of the character read. + * + * This function assumes input is valid UTF-8, + * and that there are enough characters in front of @s. + */ +static int utf8_read_char(const char *s, uchar_t *out) +{ + const unsigned char *c = (const unsigned char*) s; + + assert(utf8_validate_cz(s)); + + if (c[0] <= 0x7F) { + /* 00..7F */ + *out = c[0]; + return 1; + } else if (c[0] <= 0xDF) { + /* C2..DF (unless input is invalid) */ + *out = ((uchar_t)c[0] & 0x1F) << 6 | + ((uchar_t)c[1] & 0x3F); + return 2; + } else if (c[0] <= 0xEF) { + /* E0..EF */ + *out = ((uchar_t)c[0] & 0xF) << 12 | + ((uchar_t)c[1] & 0x3F) << 6 | + ((uchar_t)c[2] & 0x3F); + return 3; + } else { + /* F0..F4 (unless input is invalid) */ + *out = ((uchar_t)c[0] & 0x7) << 18 | + ((uchar_t)c[1] & 0x3F) << 12 | + ((uchar_t)c[2] & 0x3F) << 6 | + ((uchar_t)c[3] & 0x3F); + return 4; + } +} + +/* + * Write a single UTF-8 character to @s, + * returning the length, in bytes, of the character written. + * + * @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF. + * + * This function will write up to 4 bytes to @out. + */ +static int utf8_write_char(uchar_t unicode, char *out) +{ + unsigned char *o = (unsigned char*) out; + + assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF)); + + if (unicode <= 0x7F) { + /* U+0000..U+007F */ + *o++ = unicode; + return 1; + } else if (unicode <= 0x7FF) { + /* U+0080..U+07FF */ + *o++ = 0xC0 | unicode >> 6; + *o++ = 0x80 | (unicode & 0x3F); + return 2; + } else if (unicode <= 0xFFFF) { + /* U+0800..U+FFFF */ + *o++ = 0xE0 | unicode >> 12; + *o++ = 0x80 | (unicode >> 6 & 0x3F); + *o++ = 0x80 | (unicode & 0x3F); + return 3; + } else { + /* U+10000..U+10FFFF */ + *o++ = 0xF0 | unicode >> 18; + *o++ = 0x80 | (unicode >> 12 & 0x3F); + *o++ = 0x80 | (unicode >> 6 & 0x3F); + *o++ = 0x80 | (unicode & 0x3F); + return 4; + } +} + +/* + * Compute the Unicode codepoint of a UTF-16 surrogate pair. + * + * @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF. + * If they aren't, this function returns false. + */ +static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uchar_t *unicode) +{ + if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) { + *unicode = 0x10000 + ((((uchar_t)uc & 0x3FF) << 10) | (lc & 0x3FF)); + return true; + } else { + return false; + } +} + +/* + * Construct a UTF-16 surrogate pair given a Unicode codepoint. + * + * @unicode must be U+10000..U+10FFFF. + */ +static void to_surrogate_pair(uchar_t unicode, uint16_t *uc, uint16_t *lc) +{ + uchar_t n; + + assert(unicode >= 0x10000 && unicode <= 0x10FFFF); + + n = unicode - 0x10000; + *uc = ((n >> 10) & 0x3FF) | 0xD800; + *lc = (n & 0x3FF) | 0xDC00; +} + +#define is_space(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == ' ') +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static bool parse_value (const char **sp, JsonNode **out); +static bool parse_string (const char **sp, char **out); +static bool parse_number (const char **sp, double *out); +static bool parse_array (const char **sp, JsonNode **out); +static bool parse_object (const char **sp, JsonNode **out); +static bool parse_hex16 (const char **sp, uint16_t *out); + +static bool expect_literal (const char **sp, const char *str); +static void skip_space (const char **sp); + +static void emit_value (SB *out, const JsonNode *node); +static void emit_value_indented (SB *out, const JsonNode *node, const char *space, int indent_level); +static void emit_string (SB *out, const char *str); +static void emit_number (SB *out, double num); +static void emit_array (SB *out, const JsonNode *array); +static void emit_array_indented (SB *out, const JsonNode *array, const char *space, int indent_level); +static void emit_object (SB *out, const JsonNode *object); +static void emit_object_indented (SB *out, const JsonNode *object, const char *space, int indent_level); + +static int write_hex16(char *out, uint16_t val); + +static JsonNode *mknode(JsonTag tag); +static void append_node(JsonNode *parent, JsonNode *child); +static void prepend_node(JsonNode *parent, JsonNode *child); +static void append_member(JsonNode *object, char *key, JsonNode *value); + +/* Assertion-friendly validity checks */ +static bool tag_is_valid(unsigned int tag); +static bool number_is_valid(const char *num); + +JsonNode *json_decode(const char *json) +{ + const char *s = json; + JsonNode *ret; + + skip_space(&s); + if (!parse_value(&s, &ret)) + return NULL; + + skip_space(&s); + if (*s != 0) { + json_delete(ret); + return NULL; + } + + return ret; +} + +char *json_encode(const JsonNode *node) +{ + return json_stringify(node, NULL); +} + +char *json_encode_string(const char *str) +{ + SB sb; + sb_init(&sb); + + emit_string(&sb, str); + + return sb_finish(&sb); +} + +char *json_stringify(const JsonNode *node, const char *space) +{ + SB sb; + sb_init(&sb); + + if (space != NULL) + emit_value_indented(&sb, node, space, 0); + else + emit_value(&sb, node); + + return sb_finish(&sb); +} + +void json_delete(JsonNode *node) +{ + if (node != NULL) { + json_remove_from_parent(node); + + switch (node->tag) { + case JSON_STRING: + free(node->string_); + break; + case JSON_ARRAY: + case JSON_OBJECT: + { + JsonNode *child, *next; + for (child = node->children.head; child != NULL; child = next) { + next = child->next; + json_delete(child); + } + break; + } + default:; + } + + free(node); + } +} + +bool json_validate(const char *json) +{ + const char *s = json; + + skip_space(&s); + if (!parse_value(&s, NULL)) + return false; + + skip_space(&s); + if (*s != 0) + return false; + + return true; +} + +// We return the number of elements or -1 if there was a problem +int json_array_length(JsonNode *array) { + + int result = 0; + + // The node should not be null and it should be an array + if (array == NULL || array->tag != JSON_ARRAY) + return -1; + + // Loop and count! + JsonNode *element; + json_foreach(element, array) { + result++; + } + + return result; +} + +JsonNode *json_find_element(JsonNode *array, int index) +{ + JsonNode *element; + int i = 0; + + if (array == NULL || array->tag != JSON_ARRAY) + return NULL; + + json_foreach(element, array) { + if (i == index) + return element; + i++; + } + + return NULL; +} + +JsonNode *json_find_member(JsonNode *object, const char *name) +{ + JsonNode *member; + + if (object == NULL || object->tag != JSON_OBJECT) + return NULL; + + json_foreach(member, object) + if (strcmp(member->key, name) == 0) + return member; + + return NULL; +} + +JsonNode *json_first_child(const JsonNode *node) +{ + if (node != NULL && (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT)) + return node->children.head; + return NULL; +} + +static JsonNode *mknode(JsonTag tag) +{ + JsonNode *ret = (JsonNode*) calloc(1, sizeof(JsonNode)); + if (ret == NULL) + out_of_memory(); + ret->tag = tag; + return ret; +} + +JsonNode *json_mknull(void) +{ + return mknode(JSON_NULL); +} + +JsonNode *json_mkbool(bool b) +{ + JsonNode *ret = mknode(JSON_BOOL); + ret->bool_ = b; + return ret; +} + +static JsonNode *mkstring(char *s) +{ + JsonNode *ret = mknode(JSON_STRING); + ret->string_ = s; + return ret; +} + +JsonNode *json_mkstring(const char *s) +{ + return mkstring(json_strdup(s)); +} + +JsonNode *json_mknumber(double n) +{ + JsonNode *node = mknode(JSON_NUMBER); + node->number_ = n; + return node; +} + +JsonNode *json_mkarray(void) +{ + return mknode(JSON_ARRAY); +} + +JsonNode *json_mkobject(void) +{ + return mknode(JSON_OBJECT); +} + +static void append_node(JsonNode *parent, JsonNode *child) +{ + child->parent = parent; + child->prev = parent->children.tail; + child->next = NULL; + + if (parent->children.tail != NULL) + parent->children.tail->next = child; + else + parent->children.head = child; + parent->children.tail = child; +} + +static void prepend_node(JsonNode *parent, JsonNode *child) +{ + child->parent = parent; + child->prev = NULL; + child->next = parent->children.head; + + if (parent->children.head != NULL) + parent->children.head->prev = child; + else + parent->children.tail = child; + parent->children.head = child; +} + +static void append_member(JsonNode *object, char *key, JsonNode *value) +{ + value->key = key; + append_node(object, value); +} + +void json_append_element(JsonNode *array, JsonNode *element) +{ + assert(array->tag == JSON_ARRAY); + assert(element->parent == NULL); + + append_node(array, element); +} + +void json_prepend_element(JsonNode *array, JsonNode *element) +{ + assert(array->tag == JSON_ARRAY); + assert(element->parent == NULL); + + prepend_node(array, element); +} + +void json_append_member(JsonNode *object, const char *key, JsonNode *value) +{ + assert(object->tag == JSON_OBJECT); + assert(value->parent == NULL); + + append_member(object, json_strdup(key), value); +} + +void json_prepend_member(JsonNode *object, const char *key, JsonNode *value) +{ + assert(object->tag == JSON_OBJECT); + assert(value->parent == NULL); + + value->key = json_strdup(key); + prepend_node(object, value); +} + +void json_remove_from_parent(JsonNode *node) +{ + JsonNode *parent = node->parent; + + if (parent != NULL) { + if (node->prev != NULL) + node->prev->next = node->next; + else + parent->children.head = node->next; + if (node->next != NULL) + node->next->prev = node->prev; + else + parent->children.tail = node->prev; + + free(node->key); + + node->parent = NULL; + node->prev = node->next = NULL; + node->key = NULL; + } +} + +static bool parse_value(const char **sp, JsonNode **out) +{ + const char *s = *sp; + + switch (*s) { + case 'n': + if (expect_literal(&s, "null")) { + if (out) + *out = json_mknull(); + *sp = s; + return true; + } + return false; + + case 'f': + if (expect_literal(&s, "false")) { + if (out) + *out = json_mkbool(false); + *sp = s; + return true; + } + return false; + + case 't': + if (expect_literal(&s, "true")) { + if (out) + *out = json_mkbool(true); + *sp = s; + return true; + } + return false; + + case '"': { + char *str; + if (parse_string(&s, out ? &str : NULL)) { + if (out) + *out = mkstring(str); + *sp = s; + return true; + } + return false; + } + + case '[': + if (parse_array(&s, out)) { + *sp = s; + return true; + } + return false; + + case '{': + if (parse_object(&s, out)) { + *sp = s; + return true; + } + return false; + + default: { + double num; + if (parse_number(&s, out ? &num : NULL)) { + if (out) + *out = json_mknumber(num); + *sp = s; + return true; + } + return false; + } + } +} + +static bool parse_array(const char **sp, JsonNode **out) +{ + const char *s = *sp; + JsonNode *ret = out ? json_mkarray() : NULL; + JsonNode *element; + + if (*s++ != '[') + goto failure; + skip_space(&s); + + if (*s == ']') { + s++; + goto success; + } + + for (;;) { + if (!parse_value(&s, out ? &element : NULL)) + goto failure; + skip_space(&s); + + if (out) + json_append_element(ret, element); + + if (*s == ']') { + s++; + goto success; + } + + if (*s++ != ',') + goto failure; + skip_space(&s); + } + +success: + *sp = s; + if (out) + *out = ret; + return true; + +failure: + json_delete(ret); + return false; +} + +static bool parse_object(const char **sp, JsonNode **out) +{ + const char *s = *sp; + JsonNode *ret = out ? json_mkobject() : NULL; + char *key; + JsonNode *value; + + if (*s++ != '{') + goto failure; + skip_space(&s); + + if (*s == '}') { + s++; + goto success; + } + + for (;;) { + if (!parse_string(&s, out ? &key : NULL)) + goto failure; + skip_space(&s); + + if (*s++ != ':') + goto failure_free_key; + skip_space(&s); + + if (!parse_value(&s, out ? &value : NULL)) + goto failure_free_key; + skip_space(&s); + + if (out) + append_member(ret, key, value); + + if (*s == '}') { + s++; + goto success; + } + + if (*s++ != ',') + goto failure; + skip_space(&s); + } + +success: + *sp = s; + if (out) + *out = ret; + return true; + +failure_free_key: + if (out) + free(key); +failure: + json_delete(ret); + return false; +} + +bool parse_string(const char **sp, char **out) +{ + const char *s = *sp; + SB sb; + char throwaway_buffer[4]; + /* enough space for a UTF-8 character */ + char *b; + + if (*s++ != '"') + return false; + + if (out) { + sb_init(&sb); + sb_need(&sb, 4); + b = sb.cur; + } else { + b = throwaway_buffer; + } + + while (*s != '"') { + unsigned char c = *s++; + + /* Parse next character, and write it to b. */ + if (c == '\\') { + c = *s++; + switch (c) { + case '"': + case '\\': + case '/': + *b++ = c; + break; + case 'b': + *b++ = '\b'; + break; + case 'f': + *b++ = '\f'; + break; + case 'n': + *b++ = '\n'; + break; + case 'r': + *b++ = '\r'; + break; + case 't': + *b++ = '\t'; + break; + case 'u': + { + uint16_t uc, lc; + uchar_t unicode; + + if (!parse_hex16(&s, &uc)) + goto failed; + + if (uc >= 0xD800 && uc <= 0xDFFF) { + /* Handle UTF-16 surrogate pair. */ + if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc)) + goto failed; /* Incomplete surrogate pair. */ + if (!from_surrogate_pair(uc, lc, &unicode)) + goto failed; /* Invalid surrogate pair. */ + } else if (uc == 0) { + /* Disallow "\u0000". */ + goto failed; + } else { + unicode = uc; + } + + b += utf8_write_char(unicode, b); + break; + } + default: + /* Invalid escape */ + goto failed; + } + } else if (c <= 0x1F) { + /* Control characters are not allowed in string literals. */ + goto failed; + } else { + /* Validate and echo a UTF-8 character. */ + int len; + + s--; + len = utf8_validate_cz(s); + if (len == 0) + goto failed; /* Invalid UTF-8 character. */ + + while (len--) + *b++ = *s++; + } + + /* + * Update sb to know about the new bytes, + * and set up b to write another character. + */ + if (out) { + sb.cur = b; + sb_need(&sb, 4); + b = sb.cur; + } else { + b = throwaway_buffer; + } + } + s++; + + if (out) + *out = sb_finish(&sb); + *sp = s; + return true; + +failed: + if (out) + sb_free(&sb); + return false; +} + +/* + * The JSON spec says that a number shall follow this precise pattern + * (spaces and quotes added for readability): + * '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)? + * + * However, some JSON parsers are more liberal. For instance, PHP accepts + * '.5' and '1.'. JSON.parse accepts '+3'. + * + * This function takes the strict approach. + */ +bool parse_number(const char **sp, double *out) +{ + const char *s = *sp; + + /* '-'? */ + if (*s == '-') + s++; + + /* (0 | [1-9][0-9]*) */ + if (*s == '0') { + s++; + } else { + if (!is_digit(*s)) + return false; + do { + s++; + } while (is_digit(*s)); + } + + /* ('.' [0-9]+)? */ + if (*s == '.') { + s++; + if (!is_digit(*s)) + return false; + do { + s++; + } while (is_digit(*s)); + } + + /* ([Ee] [+-]? [0-9]+)? */ + if (*s == 'E' || *s == 'e') { + s++; + if (*s == '+' || *s == '-') + s++; + if (!is_digit(*s)) + return false; + do { + s++; + } while (is_digit(*s)); + } + + if (out) + *out = strtod(*sp, NULL); + + *sp = s; + return true; +} + +static void skip_space(const char **sp) +{ + const char *s = *sp; + while (is_space(*s)) + s++; + *sp = s; +} + +static void emit_value(SB *out, const JsonNode *node) +{ + assert(tag_is_valid(node->tag)); + switch (node->tag) { + case JSON_NULL: + sb_puts(out, "null"); + break; + case JSON_BOOL: + sb_puts(out, node->bool_ ? "true" : "false"); + break; + case JSON_STRING: + emit_string(out, node->string_); + break; + case JSON_NUMBER: + emit_number(out, node->number_); + break; + case JSON_ARRAY: + emit_array(out, node); + break; + case JSON_OBJECT: + emit_object(out, node); + break; + default: + assert(false); + } +} + +void emit_value_indented(SB *out, const JsonNode *node, const char *space, int indent_level) +{ + assert(tag_is_valid(node->tag)); + switch (node->tag) { + case JSON_NULL: + sb_puts(out, "null"); + break; + case JSON_BOOL: + sb_puts(out, node->bool_ ? "true" : "false"); + break; + case JSON_STRING: + emit_string(out, node->string_); + break; + case JSON_NUMBER: + emit_number(out, node->number_); + break; + case JSON_ARRAY: + emit_array_indented(out, node, space, indent_level); + break; + case JSON_OBJECT: + emit_object_indented(out, node, space, indent_level); + break; + default: + assert(false); + } +} + +static void emit_array(SB *out, const JsonNode *array) +{ + const JsonNode *element; + + sb_putc(out, '['); + json_foreach(element, array) { + emit_value(out, element); + if (element->next != NULL) + sb_putc(out, ','); + } + sb_putc(out, ']'); +} + +static void emit_array_indented(SB *out, const JsonNode *array, const char *space, int indent_level) +{ + const JsonNode *element = array->children.head; + int i; + + if (element == NULL) { + sb_puts(out, "[]"); + return; + } + + sb_puts(out, "[\n"); + while (element != NULL) { + for (i = 0; i < indent_level + 1; i++) + sb_puts(out, space); + emit_value_indented(out, element, space, indent_level + 1); + + element = element->next; + sb_puts(out, element != NULL ? ",\n" : "\n"); + } + for (i = 0; i < indent_level; i++) + sb_puts(out, space); + sb_putc(out, ']'); +} + +static void emit_object(SB *out, const JsonNode *object) +{ + const JsonNode *member; + + sb_putc(out, '{'); + json_foreach(member, object) { + emit_string(out, member->key); + sb_putc(out, ':'); + emit_value(out, member); + if (member->next != NULL) + sb_putc(out, ','); + } + sb_putc(out, '}'); +} + +static void emit_object_indented(SB *out, const JsonNode *object, const char *space, int indent_level) +{ + const JsonNode *member = object->children.head; + int i; + + if (member == NULL) { + sb_puts(out, "{}"); + return; + } + + sb_puts(out, "{\n"); + while (member != NULL) { + for (i = 0; i < indent_level + 1; i++) + sb_puts(out, space); + emit_string(out, member->key); + sb_puts(out, ": "); + emit_value_indented(out, member, space, indent_level + 1); + + member = member->next; + sb_puts(out, member != NULL ? ",\n" : "\n"); + } + for (i = 0; i < indent_level; i++) + sb_puts(out, space); + sb_putc(out, '}'); +} + +void emit_string(SB *out, const char *str) +{ + bool escape_unicode = false; + const char *s = str; + char *b; + + assert(utf8_validate(str)); + + /* + * 14 bytes is enough space to write up to two + * \uXXXX escapes and two quotation marks. + */ + sb_need(out, 14); + b = out->cur; + + *b++ = '"'; + while (*s != 0) { + unsigned char c = *s++; + + /* Encode the next character, and write it to b. */ + switch (c) { + case '"': + *b++ = '\\'; + *b++ = '"'; + break; + case '\\': + *b++ = '\\'; + *b++ = '\\'; + break; + case '\b': + *b++ = '\\'; + *b++ = 'b'; + break; + case '\f': + *b++ = '\\'; + *b++ = 'f'; + break; + case '\n': + *b++ = '\\'; + *b++ = 'n'; + break; + case '\r': + *b++ = '\\'; + *b++ = 'r'; + break; + case '\t': + *b++ = '\\'; + *b++ = 't'; + break; + default: { + int len; + + s--; + len = utf8_validate_cz(s); + + if (len == 0) { + /* + * Handle invalid UTF-8 character gracefully in production + * by writing a replacement character (U+FFFD) + * and skipping a single byte. + * + * This should never happen when assertions are enabled + * due to the assertion at the beginning of this function. + */ + assert(false); + if (escape_unicode) { + strcpy(b, "\\uFFFD"); + b += 6; + } else { + *b++ = 0xEF; + *b++ = 0xBF; + *b++ = 0xBD; + } + s++; + } else if (c < 0x1F || (c >= 0x80 && escape_unicode)) { + /* Encode using \u.... */ + uint32_t unicode; + + s += utf8_read_char(s, &unicode); + + if (unicode <= 0xFFFF) { + *b++ = '\\'; + *b++ = 'u'; + b += write_hex16(b, unicode); + } else { + /* Produce a surrogate pair. */ + uint16_t uc, lc; + assert(unicode <= 0x10FFFF); + to_surrogate_pair(unicode, &uc, &lc); + *b++ = '\\'; + *b++ = 'u'; + b += write_hex16(b, uc); + *b++ = '\\'; + *b++ = 'u'; + b += write_hex16(b, lc); + } + } else { + /* Write the character directly. */ + while (len--) + *b++ = *s++; + } + + break; + } + } + + /* + * Update *out to know about the new bytes, + * and set up b to write another encoded character. + */ + out->cur = b; + sb_need(out, 14); + b = out->cur; + } + *b++ = '"'; + + out->cur = b; +} + +static void emit_number(SB *out, double num) +{ + /* + * This isn't exactly how JavaScript renders numbers, + * but it should produce valid JSON for reasonable numbers + * preserve precision well enough, and avoid some oddities + * like 0.3 -> 0.299999999999999988898 . + */ + char buf[64]; + sprintf(buf, "%.16g", num); + + if (number_is_valid(buf)) + sb_puts(out, buf); + else + sb_puts(out, "null"); +} + +static bool tag_is_valid(unsigned int tag) +{ + return (/* tag >= JSON_NULL && */ tag <= JSON_OBJECT); +} + +static bool number_is_valid(const char *num) +{ + return (parse_number(&num, NULL) && *num == '\0'); +} + +static bool expect_literal(const char **sp, const char *str) +{ + const char *s = *sp; + + while (*str != '\0') + if (*s++ != *str++) + return false; + + *sp = s; + return true; +} + +/* + * Parses exactly 4 hex characters (capital or lowercase). + * Fails if any input chars are not [0-9A-Fa-f]. + */ +static bool parse_hex16(const char **sp, uint16_t *out) +{ + const char *s = *sp; + uint16_t ret = 0; + uint16_t i; + uint16_t tmp; + char c; + + for (i = 0; i < 4; i++) { + c = *s++; + if (c >= '0' && c <= '9') + tmp = c - '0'; + else if (c >= 'A' && c <= 'F') + tmp = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + tmp = c - 'a' + 10; + else + return false; + + ret <<= 4; + ret += tmp; + } + + if (out) + *out = ret; + *sp = s; + return true; +} + +/* + * Encodes a 16-bit number into hexadecimal, + * writing exactly 4 hex chars. + */ +static int write_hex16(char *out, uint16_t val) +{ + const char *hex = "0123456789ABCDEF"; + + *out++ = hex[(val >> 12) & 0xF]; + *out++ = hex[(val >> 8) & 0xF]; + *out++ = hex[(val >> 4) & 0xF]; + *out++ = hex[ val & 0xF]; + + return 4; +} + +bool json_check(const JsonNode *node, char errmsg[256]) +{ + #define problem(...) do { \ + if (errmsg != NULL) \ + snprintf(errmsg, 256, __VA_ARGS__); \ + return false; \ + } while (0) + + if (node->key != NULL && !utf8_validate(node->key)) + problem("key contains invalid UTF-8"); + + if (!tag_is_valid(node->tag)) + problem("tag is invalid (%u)", node->tag); + + if (node->tag == JSON_BOOL) { + if (node->bool_ != false && node->bool_ != true) + problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true); + } else if (node->tag == JSON_STRING) { + if (node->string_ == NULL) + problem("string_ is NULL"); + if (!utf8_validate(node->string_)) + problem("string_ contains invalid UTF-8"); + } else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) { + JsonNode *head = node->children.head; + JsonNode *tail = node->children.tail; + + if (head == NULL || tail == NULL) { + if (head != NULL) + problem("tail is NULL, but head is not"); + if (tail != NULL) + problem("head is NULL, but tail is not"); + } else { + JsonNode *child; + JsonNode *last = NULL; + + if (head->prev != NULL) + problem("First child's prev pointer is not NULL"); + + for (child = head; child != NULL; last = child, child = child->next) { + if (child == node) + problem("node is its own child"); + if (child->next == child) + problem("child->next == child (cycle)"); + if (child->next == head) + problem("child->next == head (cycle)"); + + if (child->parent != node) + problem("child does not point back to parent"); + if (child->next != NULL && child->next->prev != child) + problem("child->next does not point back to child"); + + if (node->tag == JSON_ARRAY && child->key != NULL) + problem("Array element's key is not NULL"); + if (node->tag == JSON_OBJECT && child->key == NULL) + problem("Object member's key is NULL"); + + if (!json_check(child, errmsg)) + return false; + } + + if (last != tail) + problem("tail does not match pointer found by starting at head and following next links"); + } + } + + return true; + + #undef problem +} \ No newline at end of file diff --git a/v2/internal/ffenestri/json.h b/v2/internal/ffenestri/json.h new file mode 100644 index 000000000..aaf711f8a --- /dev/null +++ b/v2/internal/ffenestri/json.h @@ -0,0 +1,122 @@ +/* + Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Source: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD +*/ + +#ifndef CCAN_JSON_H +#define CCAN_JSON_H + +#include +#include + +typedef enum { + JSON_NULL, + JSON_BOOL, + JSON_STRING, + JSON_NUMBER, + JSON_ARRAY, + JSON_OBJECT, +} JsonTag; + +typedef struct JsonNode JsonNode; + +struct JsonNode +{ + /* only if parent is an object or array (NULL otherwise) */ + JsonNode *parent; + JsonNode *prev, *next; + + /* only if parent is an object (NULL otherwise) */ + char *key; /* Must be valid UTF-8. */ + + JsonTag tag; + union { + /* JSON_BOOL */ + bool bool_; + + /* JSON_STRING */ + char *string_; /* Must be valid UTF-8. */ + + /* JSON_NUMBER */ + double number_; + + /* JSON_ARRAY */ + /* JSON_OBJECT */ + struct { + JsonNode *head, *tail; + } children; + }; +}; + +/*** Encoding, decoding, and validation ***/ + +JsonNode *json_decode (const char *json); +char *json_encode (const JsonNode *node); +char *json_encode_string (const char *str); +char *json_stringify (const JsonNode *node, const char *space); +void json_delete (JsonNode *node); + +bool json_validate (const char *json); + +/*** Lookup and traversal ***/ + +JsonNode *json_find_element (JsonNode *array, int index); +JsonNode *json_find_member (JsonNode *object, const char *key); + +JsonNode *json_first_child (const JsonNode *node); + +#define json_foreach(i, object_or_array) \ + for ((i) = json_first_child(object_or_array); \ + (i) != NULL; \ + (i) = (i)->next) + +/*** Construction and manipulation ***/ + +JsonNode *json_mknull(void); +JsonNode *json_mkbool(bool b); +JsonNode *json_mkstring(const char *s); +JsonNode *json_mknumber(double n); +JsonNode *json_mkarray(void); +JsonNode *json_mkobject(void); + +void json_append_element(JsonNode *array, JsonNode *element); +void json_prepend_element(JsonNode *array, JsonNode *element); +void json_append_member(JsonNode *object, const char *key, JsonNode *value); +void json_prepend_member(JsonNode *object, const char *key, JsonNode *value); + +void json_remove_from_parent(JsonNode *node); + +/*** Debugging ***/ + +/* + * Look for structure and encoding problems in a JsonNode or its descendents. + * + * If a problem is detected, return false, writing a description of the problem + * to errmsg (unless errmsg is NULL). + */ +bool json_check(const JsonNode *node, char errmsg[256]); + +// Added by Lea Anthony 28/11/2020 +int json_array_length(JsonNode *array); + +#endif \ No newline at end of file diff --git a/v2/internal/ffenestri/menu_darwin.c b/v2/internal/ffenestri/menu_darwin.c new file mode 100644 index 000000000..282d3331e --- /dev/null +++ b/v2/internal/ffenestri/menu_darwin.c @@ -0,0 +1,1001 @@ +// +// Created by Lea Anthony on 6/1/21. +// + +#include "ffenestri_darwin.h" +#include "menu_darwin.h" +#include "contextmenus_darwin.h" +#include "common.h" + +// NewMenu creates a new Menu struct, saving the given menu structure as JSON +Menu* NewMenu(JsonNode *menuData) { + + Menu *result = malloc(sizeof(Menu)); + + result->processedMenu = menuData; + + // No title by default + result->title = ""; + + // Initialise menuCallbackDataCache + vec_init(&result->callbackDataCache); + + // Allocate MenuItem Map + if( 0 != hashmap_create((const unsigned)16, &result->menuItemMap)) { + ABORT("[NewMenu] Not enough memory to allocate menuItemMap!"); + } + // Allocate the Radio Group Map + if( 0 != hashmap_create((const unsigned)4, &result->radioGroupMap)) { + ABORT("[NewMenu] Not enough memory to allocate radioGroupMap!"); + } + + // Init other members + result->menu = NULL; + result->parentData = NULL; + + return result; +} + +Menu* NewApplicationMenu(const char *menuAsJSON) { + + // Parse the menu json + JsonNode *processedMenu = json_decode(menuAsJSON); + if( processedMenu == NULL ) { + // Parse error! + ABORT("Unable to parse Menu JSON: %s", menuAsJSON); + } + + Menu *result = NewMenu(processedMenu); + result->menuType = ApplicationMenuType; + return result; +} + +MenuItemCallbackData* CreateMenuItemCallbackData(Menu *menu, id menuItem, const char *menuID, enum MenuItemType menuItemType) { + MenuItemCallbackData* result = malloc(sizeof(MenuItemCallbackData)); + + result->menu = menu; + result->menuID = menuID; + result->menuItem = menuItem; + result->menuItemType = menuItemType; + + // Store reference to this so we can destroy later + vec_push(&menu->callbackDataCache, result); + + return result; +} + +void DeleteMenu(Menu *menu) { + + // Free menu item hashmap + hashmap_destroy(&menu->menuItemMap); + + // Free radio group members + if( hashmap_num_entries(&menu->radioGroupMap) > 0 ) { + if (0 != hashmap_iterate_pairs(&menu->radioGroupMap, freeHashmapItem, NULL)) { + ABORT("[DeleteMenu] Failed to release radioGroupMap entries!"); + } + } + + // Free radio groups hashmap + hashmap_destroy(&menu->radioGroupMap); + + // Free up the processed menu memory + if (menu->processedMenu != NULL) { + json_delete(menu->processedMenu); + menu->processedMenu = NULL; + } + + // Release the callback data memory + vector + int i; MenuItemCallbackData* callbackData; + vec_foreach(&menu->callbackDataCache, callbackData, i) { + free(callbackData); + } + vec_deinit(&menu->callbackDataCache); + + free(menu); +} + +// Creates a JSON message for the given menuItemID and data +const char* createMenuClickedMessage(const char *menuItemID, const char *data, enum MenuType menuType, const char *parentID) { + + JsonNode *jsonObject = json_mkobject(); + if (menuItemID == NULL ) { + ABORT("Item ID NULL for menu!!\n"); + } + json_append_member(jsonObject, "menuItemID", json_mkstring(menuItemID)); + json_append_member(jsonObject, "menuType", json_mkstring(MenuTypeAsString[(int)menuType])); + if (data != NULL) { + json_append_member(jsonObject, "data", json_mkstring(data)); + } + if (parentID != NULL) { + json_append_member(jsonObject, "parentID", json_mkstring(parentID)); + } + const char *payload = json_encode(jsonObject); + json_delete(jsonObject); + const char *result = concat("MC", payload); + MEMFREE(payload); + return result; +} + +// Callback for text menu items +void menuItemCallback(id self, SEL cmd, id sender) { + MenuItemCallbackData *callbackData = (MenuItemCallbackData *)msg_reg(msg_reg(sender, s("representedObject")), s("pointerValue")); + const char *message; + + // Update checkbox / radio item + if( callbackData->menuItemType == Checkbox) { + // Toggle state + bool state = msg_reg(callbackData->menuItem, s("state")); + msg_int(callbackData->menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn)); + } else if( callbackData->menuItemType == Radio ) { + // Check the menu items' current state + bool selected = (bool)msg_reg(callbackData->menuItem, s("state")); + + // If it's already selected, exit early + if (selected) return; + + // Get this item's radio group members and turn them off + id *members = (id*)hashmap_get(&(callbackData->menu->radioGroupMap), (char*)callbackData->menuID, strlen(callbackData->menuID)); + + // Uncheck all members of the group + id thisMember = members[0]; + int count = 0; + while(thisMember != NULL) { + msg_int(thisMember, s("setState:"), NSControlStateValueOff); + count = count + 1; + thisMember = members[count]; + } + + // check the selected menu item + msg_int(callbackData->menuItem, s("setState:"), NSControlStateValueOn); + } + + const char *menuID = callbackData->menuID; + const char *data = NULL; + enum MenuType menuType = callbackData->menu->menuType; + const char *parentID = NULL; + + // Generate message to send to backend + if( menuType == ContextMenuType ) { + // Get the context menu data from the menu + ContextMenu* contextMenu = (ContextMenu*) callbackData->menu->parentData; + data = contextMenu->contextMenuData; + parentID = contextMenu->ID; + } else if ( menuType == TrayMenuType ) { + parentID = (const char*) callbackData->menu->parentData; + } + + message = createMenuClickedMessage(menuID, data, menuType, parentID); + + // Notify the backend + messageFromWindowCallback(message); + MEMFREE(message); +} + +id processAcceleratorKey(const char *key) { + + // Guard against no accelerator key + if( key == NULL ) { + return str(""); + } + + if( STREQ(key, "backspace") ) { + return strunicode(0x0008); + } + if( STREQ(key, "tab") ) { + return strunicode(0x0009); + } + if( STREQ(key, "return") ) { + return strunicode(0x000d); + } + if( STREQ(key, "enter") ) { + return strunicode(0x000d); + } + if( STREQ(key, "escape") ) { + return strunicode(0x001b); + } + if( STREQ(key, "left") ) { + return strunicode(0x001c); + } + if( STREQ(key, "right") ) { + return strunicode(0x001d); + } + if( STREQ(key, "up") ) { + return strunicode(0x001e); + } + if( STREQ(key, "down") ) { + return strunicode(0x001f); + } + if( STREQ(key, "space") ) { + return strunicode(0x0020); + } + if( STREQ(key, "delete") ) { + return strunicode(0x007f); + } + if( STREQ(key, "home") ) { + return strunicode(0x2196); + } + if( STREQ(key, "end") ) { + return strunicode(0x2198); + } + if( STREQ(key, "page up") ) { + return strunicode(0x21de); + } + if( STREQ(key, "page down") ) { + return strunicode(0x21df); + } + if( STREQ(key, "f1") ) { + return strunicode(0xf704); + } + if( STREQ(key, "f2") ) { + return strunicode(0xf705); + } + if( STREQ(key, "f3") ) { + return strunicode(0xf706); + } + if( STREQ(key, "f4") ) { + return strunicode(0xf707); + } + if( STREQ(key, "f5") ) { + return strunicode(0xf708); + } + if( STREQ(key, "f6") ) { + return strunicode(0xf709); + } + if( STREQ(key, "f7") ) { + return strunicode(0xf70a); + } + if( STREQ(key, "f8") ) { + return strunicode(0xf70b); + } + if( STREQ(key, "f9") ) { + return strunicode(0xf70c); + } + if( STREQ(key, "f10") ) { + return strunicode(0xf70d); + } + if( STREQ(key, "f11") ) { + return strunicode(0xf70e); + } + if( STREQ(key, "f12") ) { + return strunicode(0xf70f); + } + if( STREQ(key, "f13") ) { + return strunicode(0xf710); + } + if( STREQ(key, "f14") ) { + return strunicode(0xf711); + } + if( STREQ(key, "f15") ) { + return strunicode(0xf712); + } + if( STREQ(key, "f16") ) { + return strunicode(0xf713); + } + if( STREQ(key, "f17") ) { + return strunicode(0xf714); + } + if( STREQ(key, "f18") ) { + return strunicode(0xf715); + } + if( STREQ(key, "f19") ) { + return strunicode(0xf716); + } + if( STREQ(key, "f20") ) { + return strunicode(0xf717); + } + if( STREQ(key, "f21") ) { + return strunicode(0xf718); + } + if( STREQ(key, "f22") ) { + return strunicode(0xf719); + } + if( STREQ(key, "f23") ) { + return strunicode(0xf71a); + } + if( STREQ(key, "f24") ) { + return strunicode(0xf71b); + } + if( STREQ(key, "f25") ) { + return strunicode(0xf71c); + } + if( STREQ(key, "f26") ) { + return strunicode(0xf71d); + } + if( STREQ(key, "f27") ) { + return strunicode(0xf71e); + } + if( STREQ(key, "f28") ) { + return strunicode(0xf71f); + } + if( STREQ(key, "f29") ) { + return strunicode(0xf720); + } + if( STREQ(key, "f30") ) { + return strunicode(0xf721); + } + if( STREQ(key, "f31") ) { + return strunicode(0xf722); + } + if( STREQ(key, "f32") ) { + return strunicode(0xf723); + } + if( STREQ(key, "f33") ) { + return strunicode(0xf724); + } + if( STREQ(key, "f34") ) { + return strunicode(0xf725); + } + if( STREQ(key, "f35") ) { + return strunicode(0xf726); + } +// if( STREQ(key, "Insert") ) { +// return strunicode(0xf727); +// } +// if( STREQ(key, "PrintScreen") ) { +// return strunicode(0xf72e); +// } +// if( STREQ(key, "ScrollLock") ) { +// return strunicode(0xf72f); +// } + if( STREQ(key, "numLock") ) { + return strunicode(0xf739); + } + + return str(key); +} + + +void addSeparator(id menu) { + id item = msg_reg(c("NSMenuItem"), s("separatorItem")); + msg_id(menu, s("addItem:"), item); +} + +id createMenuItemNoAutorelease( id title, const char *action, const char *key) { + id item = ALLOC("NSMenuItem"); + ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key)); + return item; +} + +id createMenuItem(id title, const char *action, const char *key) { + id item = ALLOC("NSMenuItem"); + ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key)); + msg_reg(item, s("autorelease")); + return item; +} + +id addMenuItem(id menu, const char *title, const char *action, const char *key, bool disabled) { + id item = createMenuItem(str(title), action, key); + msg_bool(item, s("setEnabled:"), !disabled); + msg_id(menu, s("addItem:"), item); + return item; +} + +id createMenu(id title) { + id menu = ALLOC("NSMenu"); + msg_id(menu, s("initWithTitle:"), title); + msg_bool(menu, s("setAutoenablesItems:"), NO); + msg_reg(menu, s("autorelease")); + return menu; +} + +void createDefaultAppMenu(id parentMenu) { +// App Menu + id appName = msg_reg(msg_reg(c("NSProcessInfo"), s("processInfo")), s("processName")); + id appMenuItem = createMenuItemNoAutorelease(appName, NULL, ""); + id appMenu = createMenu(appName); + + msg_id(appMenuItem, s("setSubmenu:"), appMenu); + msg_id(parentMenu, s("addItem:"), appMenuItem); + + id title = msg_id(str("Hide "), s("stringByAppendingString:"), appName); + id item = createMenuItem(title, "hide:", "h"); + msg_id(appMenu, s("addItem:"), item); + + id hideOthers = addMenuItem(appMenu, "Hide Others", "hideOtherApplications:", "h", FALSE); + msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand)); + + addMenuItem(appMenu, "Show All", "unhideAllApplications:", "", FALSE); + + addSeparator(appMenu); + + title = msg_id(str("Quit "), s("stringByAppendingString:"), appName); + item = createMenuItem(title, "terminate:", "q"); + msg_id(appMenu, s("addItem:"), item); +} + +void createDefaultEditMenu(id parentMenu) { + // Edit Menu + id editMenuItem = createMenuItemNoAutorelease(str("Edit"), NULL, ""); + id editMenu = createMenu(str("Edit")); + + msg_id(editMenuItem, s("setSubmenu:"), editMenu); + msg_id(parentMenu, s("addItem:"), editMenuItem); + + addMenuItem(editMenu, "Undo", "undo:", "z", FALSE); + addMenuItem(editMenu, "Redo", "redo:", "y", FALSE); + addSeparator(editMenu); + addMenuItem(editMenu, "Cut", "cut:", "x", FALSE); + addMenuItem(editMenu, "Copy", "copy:", "c", FALSE); + addMenuItem(editMenu, "Paste", "paste:", "v", FALSE); + addMenuItem(editMenu, "Select All", "selectAll:", "a", FALSE); +} + +void processMenuRole(Menu *menu, id parentMenu, JsonNode *item) { + const char *roleName = item->string_; + + if ( STREQ(roleName, "appMenu") ) { + createDefaultAppMenu(parentMenu); + return; + } + if ( STREQ(roleName, "editMenu")) { + createDefaultEditMenu(parentMenu); + return; + } + if ( STREQ(roleName, "hide")) { + addMenuItem(parentMenu, "Hide Window", "hide:", "h", FALSE); + return; + } + if ( STREQ(roleName, "hideothers")) { + id hideOthers = addMenuItem(parentMenu, "Hide Others", "hideOtherApplications:", "h", FALSE); + msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand)); + return; + } + if ( STREQ(roleName, "unhide")) { + addMenuItem(parentMenu, "Show All", "unhideAllApplications:", "", FALSE); + return; + } + if ( STREQ(roleName, "front")) { + addMenuItem(parentMenu, "Bring All to Front", "arrangeInFront:", "", FALSE); + return; + } + if ( STREQ(roleName, "undo")) { + addMenuItem(parentMenu, "Undo", "undo:", "z", FALSE); + return; + } + if ( STREQ(roleName, "redo")) { + addMenuItem(parentMenu, "Redo", "redo:", "y", FALSE); + return; + } + if ( STREQ(roleName, "cut")) { + addMenuItem(parentMenu, "Cut", "cut:", "x", FALSE); + return; + } + if ( STREQ(roleName, "copy")) { + addMenuItem(parentMenu, "Copy", "copy:", "c", FALSE); + return; + } + if ( STREQ(roleName, "paste")) { + addMenuItem(parentMenu, "Paste", "paste:", "v", FALSE); + return; + } + if ( STREQ(roleName, "delete")) { + addMenuItem(parentMenu, "Delete", "delete:", "", FALSE); + return; + } + if( STREQ(roleName, "pasteandmatchstyle")) { + id pasteandmatchstyle = addMenuItem(parentMenu, "Paste and Match Style", "pasteandmatchstyle:", "v", FALSE); + msg_int(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand)); + } + if ( STREQ(roleName, "selectall")) { + addMenuItem(parentMenu, "Select All", "selectAll:", "a", FALSE); + return; + } + if ( STREQ(roleName, "minimize")) { + addMenuItem(parentMenu, "Minimize", "miniaturize:", "m", FALSE); + return; + } + if ( STREQ(roleName, "zoom")) { + addMenuItem(parentMenu, "Zoom", "performZoom:", "", FALSE); + return; + } + if ( STREQ(roleName, "quit")) { + addMenuItem(parentMenu, "Quit (More work TBD)", "terminate:", "q", FALSE); + return; + } + if ( STREQ(roleName, "togglefullscreen")) { + addMenuItem(parentMenu, "Toggle Full Screen", "toggleFullScreen:", "f", FALSE); + return; + } + +} + +// This converts a string array of modifiers into the +// equivalent MacOS Modifier Flags +unsigned long parseModifiers(const char **modifiers) { + + // Our result is a modifier flag list + unsigned long result = 0; + + const char *thisModifier = modifiers[0]; + int count = 0; + while( thisModifier != NULL ) { + + // Determine flags + if( STREQ(thisModifier, "cmdorctrl") ) { + result |= NSEventModifierFlagCommand; + } + if( STREQ(thisModifier, "optionoralt") ) { + result |= NSEventModifierFlagOption; + } + if( STREQ(thisModifier, "shift") ) { + result |= NSEventModifierFlagShift; + } + if( STREQ(thisModifier, "super") ) { + result |= NSEventModifierFlagCommand; + } + if( STREQ(thisModifier, "ctrl") ) { + result |= NSEventModifierFlagControl; + } + count++; + thisModifier = modifiers[count]; + } + return result; +} + +id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *acceleratorkey) { + id item = ALLOC("NSMenuItem"); + + // Store the item in the menu item map + hashmap_put(&menu->menuItemMap, (char*)menuid, strlen(menuid), item); + + // Create a MenuItemCallbackData + MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Radio); + + id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback); + msg_id(item, s("setRepresentedObject:"), wrappedId); + + id key = processAcceleratorKey(acceleratorkey); + + ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), key); + + msg_bool(item, s("setEnabled:"), !disabled); + msg_reg(item, s("autorelease")); + msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff)); + + msg_id(parentmenu, s("addItem:"), item); + return item; + +} + +id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key) { + + id item = ALLOC("NSMenuItem"); + msg_reg(item, s("autorelease")); + + // Store the item in the menu item map + hashmap_put(&menu->menuItemMap, (char*)menuid, strlen(menuid), item); + + // Create a MenuItemCallbackData + MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Checkbox); + + id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback); + msg_id(item, s("setRepresentedObject:"), wrappedId); + ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(key)); + msg_bool(item, s("setEnabled:"), !disabled); + msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff)); + msg_id(parentmenu, s("addItem:"), item); + return item; +} + +// getColour returns the colour from a styledLabel based on the key +const char* getColour(JsonNode *styledLabelEntry, const char* key) { + JsonNode* colEntry = getJSONObject(styledLabelEntry, key); + if( colEntry == NULL ) { + return NULL; + } + return getJSONString(colEntry, "hex"); +} + +id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize) { + + // Create result + id attributedString = ALLOC_INIT("NSMutableAttributedString"); + msg_reg(attributedString, s("autorelease")); + + // Create new Dictionary + id dictionary = ALLOC_INIT("NSMutableDictionary"); + msg_reg(dictionary, s("autorelease")); + + // Use default font + CGFloat fontSizeFloat = (CGFloat)fontSize; + id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat); + + // Check user supplied font + if( STR_HAS_CHARS(fontName) ) { + id fontNameAsNSString = str(fontName); + id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat); + if( userFont != NULL ) { + font = userFont; + } + } + + id fan = lookupStringConstant(str("NSFontAttributeName")); + id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName")); + id NSBackgroundColorAttributeName = lookupStringConstant(str("NSBackgroundColorAttributeName")); + + // Loop over styled text creating NSAttributedText and appending to result + JsonNode *styledLabelEntry; + json_foreach(styledLabelEntry, styledLabel) { + + // Clear dictionary + msg_reg(dictionary, s("removeAllObjects")); + + // Add font to dictionary + msg_id_id(dictionary, s("setObject:forKey:"), font, fan); + + // Get Text + const char* thisLabel = mustJSONString(styledLabelEntry, "Label"); + + // Get foreground colour + const char *hexColour = getColour(styledLabelEntry, "FgCol"); + if( hexColour != NULL) { + unsigned short r, g, b, a; + + // white by default + r = g = b = a = 255; + int count = sscanf(hexColour, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a); + if (count > 0) { + id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"), + (CGFloat)r / (CGFloat)255.0, + (CGFloat)g / (CGFloat)255.0, + (CGFloat)b / (CGFloat)255.0, + (CGFloat)a / (CGFloat)255.0); + msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName); + } + } + + // Get background colour + hexColour = getColour(styledLabelEntry, "BgCol"); + if( hexColour != NULL) { + unsigned short r, g, b, a; + + // white by default + r = g = b = a = 255; + int count = sscanf(hexColour, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a); + if (count > 0) { + id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"), + (CGFloat)r / (CGFloat)255.0, + (CGFloat)g / (CGFloat)255.0, + (CGFloat)b / (CGFloat)255.0, + (CGFloat)a / (CGFloat)255.0); + msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName); + } + } + + // Create AttributedText + id thisString = ALLOC("NSMutableAttributedString"); + msg_reg(thisString, s("autorelease")); + msg_id_id(thisString, s("initWithString:attributes:"), str(thisLabel), dictionary); + + // Append text to result + msg_id(attributedString, s("appendAttributedString:"), thisString); + } + + return attributedString; + +} + + +id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) { + + // Create new Dictionary + id dictionary = ALLOC_INIT("NSMutableDictionary"); + CGFloat fontSizeFloat = (CGFloat)fontSize; + + // Use default font + id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat); + + // Check user supplied font + if( STR_HAS_CHARS(fontName) ) { + id fontNameAsNSString = str(fontName); + id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat); + if( userFont != NULL ) { + font = userFont; + } + } + + // Add font to dictionary + id fan = lookupStringConstant(str("NSFontAttributeName")); + msg_id_id(dictionary, s("setObject:forKey:"), font, fan); + + // RGBA + if( RGBA != NULL && strlen(RGBA) > 0) { + unsigned short r, g, b, a; + + // white by default + r = g = b = a = 255; + int count = sscanf(RGBA, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a); + if (count > 0) { + id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"), + (CGFloat)r / (CGFloat)255.0, + (CGFloat)g / (CGFloat)255.0, + (CGFloat)b / (CGFloat)255.0, + (CGFloat)a / (CGFloat)255.0); + id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName")); + msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName); + } + } + + id attributedString = ALLOC("NSMutableAttributedString"); + msg_id_id(attributedString, s("initWithString:attributes:"), str(title), dictionary); + msg_reg(attributedString, s("autorelease")); + msg_reg(dictionary, s("autorelease")); + return attributedString; +} + +id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate, JsonNode* styledLabel) { + id item = ALLOC("NSMenuItem"); + msg_reg(item, s("autorelease")); + + // Create a MenuItemCallbackData + MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text); + + id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback); + msg_id(item, s("setRepresentedObject:"), wrappedId); + + if( !alternate ) { + id key = processAcceleratorKey(acceleratorkey); + ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), + s("menuItemCallback:"), key); + } else { + ((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str("")); + } + + if( tooltip != NULL ) { + msg_id(item, s("setToolTip:"), str(tooltip)); + } + + // Process image + if( image != NULL && strlen(image) > 0) { + id nsimage = createImageFromBase64Data(image, templateImage); + msg_id(item, s("setImage:"), nsimage); + } + + id attributedString = NULL; + if( styledLabel != NULL) { + attributedString = createAttributedStringFromStyledLabel(styledLabel, fontName, fontSize); + } else { + attributedString = createAttributedString(title, fontName, fontSize, RGBA); + } + msg_id(item, s("setAttributedTitle:"), attributedString); + +//msg_id(item, s("setTitle:"), str(title)); + + msg_bool(item, s("setEnabled:"), !disabled); + + // Process modifiers + if( modifiers != NULL && !alternate) { + unsigned long modifierFlags = parseModifiers(modifiers); + ((id(*)(id, SEL, unsigned long))objc_msgSend)(item, s("setKeyEquivalentModifierMask:"), modifierFlags); + } + + // alternate + if( alternate ) { + msg_bool(item, s("setAlternate:"), true); + msg_int(item, s("setKeyEquivalentModifierMask:"), NSEventModifierFlagOption); + } + msg_id(parentMenu, s("addItem:"), item); + + return item; +} + +void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) { + + // Check if this item is hidden and if so, exit early! + bool hidden = false; + getJSONBool(item, "Hidden", &hidden); + if( hidden ) { + return; + } + + // Get the role + JsonNode *role = json_find_member(item, "Role"); + if( role != NULL ) { + processMenuRole(menu, parentMenu, role); + return; + } + + // This is a user menu. Get the common data + // Get the label + const char *label = getJSONString(item, "Label"); + if ( label == NULL) { + label = "(empty)"; + } + + // Check for a styled label + JsonNode *styledLabel = getJSONObject(item, "StyledLabel"); + + // Is this an alternate menu item? + bool alternate = false; + getJSONBool(item, "MacAlternate", &alternate); + + const char *menuid = getJSONString(item, "ID"); + if ( menuid == NULL) { + menuid = ""; + } + + bool disabled = false; + getJSONBool(item, "Disabled", &disabled); + + // Get the Accelerator + JsonNode *accelerator = json_find_member(item, "Accelerator"); + const char *acceleratorkey = NULL; + const char **modifiers = NULL; + + const char *tooltip = getJSONString(item, "Tooltip"); + const char *image = getJSONString(item, "Image"); + const char *fontName = getJSONString(item, "FontName"); + const char *RGBA = getJSONString(item, "RGBA"); + bool templateImage = false; + getJSONBool(item, "MacTemplateImage", &templateImage); + + int fontSize = 0; + getJSONInt(item, "FontSize", &fontSize); + + // If we have an accelerator + if( accelerator != NULL ) { + // Get the key + acceleratorkey = getJSONString(accelerator, "Key"); + // Check if there are modifiers + JsonNode *modifiersList = json_find_member(accelerator, "Modifiers"); + if ( modifiersList != NULL ) { + // Allocate an array of strings + int noOfModifiers = json_array_length(modifiersList); + + // Do we have any? + if (noOfModifiers > 0) { + modifiers = malloc(sizeof(const char *) * (noOfModifiers + 1)); + JsonNode *modifier; + int count = 0; + // Iterate the modifiers and save a reference to them in our new array + json_foreach(modifier, modifiersList) { + // Get modifier name + modifiers[count] = modifier->string_; + count++; + } + // Null terminate the modifier list + modifiers[count] = NULL; + } + } + } + + // Get the Type + JsonNode *type = json_find_member(item, "Type"); + if( type != NULL ) { + if( STREQ(type->string_, "Text") || STREQ(type->string_, "Submenu")) { + id thisMenuItem = processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate, styledLabel); + + // Check if this node has a submenu + JsonNode *submenu = json_find_member(item, "SubMenu"); + if( submenu != NULL ) { + // Get the label + JsonNode *menuNameNode = json_find_member(item, "Label"); + const char *name = ""; + if ( menuNameNode != NULL) { + name = menuNameNode->string_; + } + + id thisMenu = createMenu(str(name)); + + msg_id(thisMenuItem, s("setSubmenu:"), thisMenu); + + JsonNode *submenuItems = json_find_member(submenu, "Items"); + // If we have no items, just return + if ( submenuItems == NULL ) { + return; + } + + // Loop over submenu items + JsonNode *item; + json_foreach(item, submenuItems) { + // Get item label + processMenuItem(menu, thisMenu, item); + } + } + } + else if ( STREQ(type->string_, "Separator")) { + addSeparator(parentMenu); + } + else if ( STREQ(type->string_, "Checkbox")) { + // Get checked state + bool checked = false; + getJSONBool(item, "Checked", &checked); + + processCheckboxMenuItem(menu, parentMenu, label, menuid, disabled, checked, ""); + } + else if ( STREQ(type->string_, "Radio")) { + // Get checked state + bool checked = false; + getJSONBool(item, "Checked", &checked); + + processRadioMenuItem(menu, parentMenu, label, menuid, disabled, checked, ""); + } + } + + if ( modifiers != NULL ) { + free(modifiers); + } + + return; +} + +void processMenuData(Menu *menu, JsonNode *menuData) { + JsonNode *items = json_find_member(menuData, "Items"); + if( items == NULL ) { + // Parse error! + ABORT("Unable to find 'Items' in menu JSON!"); + } + + // Iterate items + JsonNode *item; + json_foreach(item, items) { + // Process each menu item + processMenuItem(menu, menu->menu, item); + } +} + +void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup) { + + int groupLength; + getJSONInt(radioGroup, "Length", &groupLength); + JsonNode *members = json_find_member(radioGroup, "Members"); + JsonNode *member; + + // Allocate array + size_t arrayLength = sizeof(id)*(groupLength+1); + id memberList[arrayLength]; + + // Build the radio group items + int count=0; + json_foreach(member, members) { + // Get menu by id + id menuItem = (id)hashmap_get(&menu->menuItemMap, (char*)member->string_, strlen(member->string_)); + // Save Member + memberList[count] = menuItem; + count = count + 1; + } + // Null terminate array + memberList[groupLength] = 0; + + // Store the members + json_foreach(member, members) { + // Copy the memberList + char *newMemberList = (char *)malloc(arrayLength); + memcpy(newMemberList, memberList, arrayLength); + // add group to each member of group + hashmap_put(&menu->radioGroupMap, member->string_, strlen(member->string_), newMemberList); + } + +} + +id GetMenu(Menu *menu) { + + // Pull out the menu data + JsonNode *menuData = json_find_member(menu->processedMenu, "Menu"); + if( menuData == NULL ) { + ABORT("Unable to find Menu data: %s", menu->processedMenu); + } + + menu->menu = createMenu(str("")); + + // Process the menu data + processMenuData(menu, menuData); + + // Create the radiogroup cache + JsonNode *radioGroups = json_find_member(menu->processedMenu, "RadioGroups"); + if( radioGroups == NULL ) { + // Parse error! + ABORT("Unable to find RadioGroups data: %s", menu->processedMenu); + } + + // Iterate radio groups + JsonNode *radioGroup; + json_foreach(radioGroup, radioGroups) { + // Get item label + processRadioGroupJSON(menu, radioGroup); + } + + return menu->menu; +} + diff --git a/v2/internal/ffenestri/menu_darwin.h b/v2/internal/ffenestri/menu_darwin.h new file mode 100644 index 000000000..a68c483bd --- /dev/null +++ b/v2/internal/ffenestri/menu_darwin.h @@ -0,0 +1,117 @@ +// +// Created by Lea Anthony on 6/1/21. +// + +#ifndef MENU_DARWIN_H +#define MENU_DARWIN_H + +#include "common.h" +#include "ffenestri_darwin.h" + +enum MenuItemType {Text = 0, Checkbox = 1, Radio = 2}; +enum MenuType {ApplicationMenuType = 0, ContextMenuType = 1, TrayMenuType = 2}; +static const char *MenuTypeAsString[] = { + "ApplicationMenu", "ContextMenu", "TrayMenu", +}; + +typedef struct _NSRange { + unsigned long location; + unsigned long length; +} NSRange; + +#define NSFontWeightUltraLight -0.8 +#define NSFontWeightThin -0.6 +#define NSFontWeightLight -0.4 +#define NSFontWeightRegular 0.0 +#define NSFontWeightMedium 0.23 +#define NSFontWeightSemibold 0.3 +#define NSFontWeightBold 0.4 +#define NSFontWeightHeavy 0.56 +#define NSFontWeightBlack 0.62 + +extern void messageFromWindowCallback(const char *); + +typedef struct { + + const char *title; + + /*** Internal ***/ + + // The decoded version of the Menu JSON + JsonNode *processedMenu; + + struct hashmap_s menuItemMap; + struct hashmap_s radioGroupMap; + + // Vector to keep track of callback data memory + vec_void_t callbackDataCache; + + // The NSMenu for this menu + id menu; + + // The parent data, eg ContextMenuStore or Tray + void *parentData; + + // The commands for the menu callbacks + const char *callbackCommand; + + // This indicates if we are an Application Menu, tray menu or context menu + enum MenuType menuType; + + +} Menu; + + +typedef struct { + id menuItem; + Menu *menu; + const char *menuID; + enum MenuItemType menuItemType; +} MenuItemCallbackData; + + + +// NewMenu creates a new Menu struct, saving the given menu structure as JSON +Menu* NewMenu(JsonNode *menuData); + +Menu* NewApplicationMenu(const char *menuAsJSON); +MenuItemCallbackData* CreateMenuItemCallbackData(Menu *menu, id menuItem, const char *menuID, enum MenuItemType menuItemType); + +void DeleteMenu(Menu *menu); + +// Creates a JSON message for the given menuItemID and data +const char* createMenuClickedMessage(const char *menuItemID, const char *data, enum MenuType menuType, const char *parentID); +// Callback for text menu items +void menuItemCallback(id self, SEL cmd, id sender); +id processAcceleratorKey(const char *key); + + +void addSeparator(id menu); +id createMenuItemNoAutorelease( id title, const char *action, const char *key); + +id createMenuItem(id title, const char *action, const char *key); + +id addMenuItem(id menu, const char *title, const char *action, const char *key, bool disabled); + +id createMenu(id title); +void createDefaultAppMenu(id parentMenu); +void createDefaultEditMenu(id parentMenu); + +void processMenuRole(Menu *menu, id parentMenu, JsonNode *item); +// This converts a string array of modifiers into the +// equivalent MacOS Modifier Flags +unsigned long parseModifiers(const char **modifiers); +id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *acceleratorkey); + +id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key); + +id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate, JsonNode* styledLabel); +void processMenuItem(Menu *menu, id parentMenu, JsonNode *item); +void processMenuData(Menu *menu, JsonNode *menuData); + +void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup); +id GetMenu(Menu *menu); +id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA); +id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize); + +#endif //ASSETS_C_MENU_DARWIN_H diff --git a/v2/internal/ffenestri/runtime_darwin.c b/v2/internal/ffenestri/runtime_darwin.c new file mode 100644 index 000000000..0d733f87a --- /dev/null +++ b/v2/internal/ffenestri/runtime_darwin.c @@ -0,0 +1,5 @@ + +// runtime.c (c) 2019-Present Lea Anthony. +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file was auto-generated. DO NOT MODIFY. +const unsigned char runtime[]={0x76, 0x61, 0x72, 0x20, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x28, 0x72, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x74, 0x5b, 0x72, 0x5d, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x5b, 0x72, 0x5d, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x74, 0x5b, 0x72, 0x5d, 0x3d, 0x7b, 0x69, 0x3a, 0x72, 0x2c, 0x6c, 0x3a, 0x21, 0x31, 0x2c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x3a, 0x7b, 0x7d, 0x7d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x5b, 0x72, 0x5d, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2c, 0x69, 0x2c, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2c, 0x65, 0x29, 0x2c, 0x69, 0x2e, 0x6c, 0x3d, 0x21, 0x30, 0x2c, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x2e, 0x6d, 0x3d, 0x6e, 0x2c, 0x65, 0x2e, 0x63, 0x3d, 0x74, 0x2c, 0x65, 0x2e, 0x64, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x72, 0x29, 0x7b, 0x65, 0x2e, 0x6f, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7c, 0x7c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x7b, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x21, 0x30, 0x2c, 0x67, 0x65, 0x74, 0x3a, 0x72, 0x7d, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x72, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x22, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x22, 0x21, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x26, 0x26, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x67, 0x26, 0x26, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x67, 0x2c, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x22, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x7d, 0x29, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x22, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x2c, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x21, 0x30, 0x7d, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x31, 0x26, 0x74, 0x26, 0x26, 0x28, 0x6e, 0x3d, 0x65, 0x28, 0x6e, 0x29, 0x29, 0x2c, 0x38, 0x26, 0x74, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x3b, 0x69, 0x66, 0x28, 0x34, 0x26, 0x74, 0x26, 0x26, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x3d, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6e, 0x26, 0x26, 0x6e, 0x26, 0x26, 0x6e, 0x2e, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x69, 0x66, 0x28, 0x65, 0x2e, 0x72, 0x28, 0x72, 0x29, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x72, 0x2c, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x2c, 0x7b, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x21, 0x30, 0x2c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x6e, 0x7d, 0x29, 0x2c, 0x32, 0x26, 0x74, 0x26, 0x26, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x21, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6e, 0x29, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x69, 0x6e, 0x20, 0x6e, 0x29, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x69, 0x2c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x5b, 0x74, 0x5d, 0x7d, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x69, 0x29, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x7d, 0x2c, 0x65, 0x2e, 0x6e, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x26, 0x26, 0x6e, 0x2e, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x3f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x7d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x7d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x2e, 0x64, 0x28, 0x74, 0x2c, 0x22, 0x61, 0x22, 0x2c, 0x74, 0x29, 0x2c, 0x74, 0x7d, 0x2c, 0x65, 0x2e, 0x6f, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x70, 0x3d, 0x22, 0x22, 0x2c, 0x65, 0x28, 0x65, 0x2e, 0x73, 0x3d, 0x30, 0x29, 0x7d, 0x28, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x22, 0x75, 0x73, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x22, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x74, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x72, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x54, 0x72, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x44, 0x65, 0x62, 0x75, 0x67, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x79, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x46, 0x61, 0x74, 0x61, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x68, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x53, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x45, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x69, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x69, 0x2c, 0x22, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x78, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x6f, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4e, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x54, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x46, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6a, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x44, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x49, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x50, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x48, 0x69, 0x64, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x41, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x68, 0x6f, 0x77, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4a, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4c, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x46, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x55, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x61, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x61, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x42, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x53, 0x61, 0x76, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x48, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x47, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x75, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x75, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x75, 0x2c, 0x22, 0x4e, 0x65, 0x77, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x6e, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x63, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x63, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x63, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x6e, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x3d, 0x7b, 0x41, 0x70, 0x70, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x22, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x22, 0x2c, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x22, 0x64, 0x61, 0x72, 0x77, 0x69, 0x6e, 0x22, 0x7d, 0x7d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x73, 0x3d, 0x5b, 0x5d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x28, 0x6e, 0x29, 0x7b, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x28, 0x6e, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x28, 0x6e, 0x29, 0x2c, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3e, 0x30, 0x29, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x30, 0x3b, 0x74, 0x3c, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x74, 0x2b, 0x2b, 0x29, 0x73, 0x5b, 0x74, 0x5d, 0x28, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x4c, 0x22, 0x2b, 0x6e, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x54, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x50, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x79, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x44, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x49, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x57, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x45, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x46, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x53, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x76, 0x61, 0x72, 0x20, 0x4f, 0x2c, 0x45, 0x3d, 0x7b, 0x54, 0x52, 0x41, 0x43, 0x45, 0x3a, 0x31, 0x2c, 0x44, 0x45, 0x42, 0x55, 0x47, 0x3a, 0x32, 0x2c, 0x49, 0x4e, 0x46, 0x4f, 0x3a, 0x33, 0x2c, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x3a, 0x34, 0x2c, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x3a, 0x35, 0x7d, 0x2c, 0x6b, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x26, 0x26, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x7c, 0x7c, 0x28, 0x65, 0x3d, 0x30, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x50, 0x72, 0x6f, 0x6d, 0x69, 0x73, 0x65, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x2c, 0x69, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3b, 0x64, 0x6f, 0x7b, 0x6f, 0x3d, 0x6e, 0x2b, 0x22, 0x2d, 0x22, 0x2b, 0x4f, 0x28, 0x29, 0x7d, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x6b, 0x5b, 0x6f, 0x5d, 0x29, 0x3b, 0x69, 0x66, 0x28, 0x65, 0x3e, 0x30, 0x29, 0x76, 0x61, 0x72, 0x20, 0x61, 0x3d, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x69, 0x28, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x43, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x49, 0x44, 0x3a, 0x20, 0x22, 0x2b, 0x6f, 0x29, 0x29, 0x7d, 0x29, 0x2c, 0x65, 0x29, 0x3b, 0x6b, 0x5b, 0x6f, 0x5d, 0x3d, 0x7b, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3a, 0x61, 0x2c, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x69, 0x2c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x3a, 0x72, 0x7d, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x75, 0x3d, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x6e, 0x2c, 0x61, 0x72, 0x67, 0x73, 0x3a, 0x74, 0x2c, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x44, 0x3a, 0x6f, 0x7d, 0x3b, 0x64, 0x28, 0x22, 0x43, 0x22, 0x2b, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x75, 0x29, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6e, 0x29, 0x7d, 0x7d, 0x29, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x57, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x22, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x20, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x74, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2c, 0x22, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x22, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x6e, 0x29, 0x3b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x79, 0x28, 0x65, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x29, 0x7d, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x69, 0x64, 0x2c, 0x69, 0x3d, 0x6b, 0x5b, 0x72, 0x5d, 0x3b, 0x69, 0x66, 0x28, 0x21, 0x69, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3d, 0x22, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x20, 0x27, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x72, 0x2c, 0x22, 0x27, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x21, 0x21, 0x21, 0x22, 0x29, 0x3b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6f, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6f, 0x29, 0x7d, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x69, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x2c, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x6b, 0x5b, 0x72, 0x5d, 0x2c, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3f, 0x69, 0x2e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3a, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x28, 0x74, 0x2e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4d, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x31, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x43, 0x28, 0x22, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x22, 0x2b, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x78, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x28, 0x22, 0x52, 0x42, 0x4f, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x63, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, 0x28, 0x6e, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x54, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6a, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x46, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x66, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x73, 0x3a, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x3a, 0x22, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x70, 0x3a, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x3a, 0x22, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x48, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4a, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x53, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4c, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x4d, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x52, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x55, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x6d, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x75, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x55, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x43, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x42, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x48, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x4f, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x3f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x3d, 0x6e, 0x65, 0x77, 0x20, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x31, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x67, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x28, 0x6e, 0x29, 0x5b, 0x30, 0x5d, 0x7d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x39, 0x30, 0x30, 0x37, 0x31, 0x39, 0x39, 0x32, 0x35, 0x34, 0x37, 0x34, 0x30, 0x39, 0x39, 0x31, 0x2a, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x29, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x7b, 0x7d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x71, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x28, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x21, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x21, 0x28, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x74, 0x29, 0x29, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x43, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x7d, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x6e, 0x29, 0x2c, 0x65, 0x3d, 0x65, 0x7c, 0x7c, 0x2d, 0x31, 0x2c, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x29, 0x2c, 0x2d, 0x31, 0x21, 0x3d, 0x3d, 0x65, 0x26, 0x26, 0x30, 0x3d, 0x3d, 0x3d, 0x28, 0x65, 0x2d, 0x3d, 0x31, 0x29, 0x7d, 0x7d, 0x2c, 0x7a, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x7a, 0x5b, 0x6e, 0x5d, 0x3d, 0x7a, 0x5b, 0x6e, 0x5d, 0x7c, 0x7c, 0x5b, 0x5d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x6e, 0x65, 0x77, 0x20, 0x71, 0x28, 0x74, 0x2c, 0x65, 0x29, 0x3b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x22, 0x50, 0x75, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3a, 0x20, 0x22, 0x2b, 0x6e, 0x29, 0x2c, 0x7a, 0x5b, 0x6e, 0x5d, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x72, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4b, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x51, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x31, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x58, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x69, 0x66, 0x28, 0x7a, 0x5b, 0x74, 0x5d, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x7a, 0x5b, 0x74, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x29, 0x2c, 0x72, 0x3d, 0x30, 0x3b, 0x72, 0x3c, 0x7a, 0x5b, 0x74, 0x5d, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x72, 0x2b, 0x3d, 0x31, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x7a, 0x5b, 0x74, 0x5d, 0x5b, 0x72, 0x5d, 0x2c, 0x6f, 0x3d, 0x6e, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3b, 0x69, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x6f, 0x29, 0x26, 0x26, 0x65, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x72, 0x2c, 0x31, 0x29, 0x7d, 0x7a, 0x5b, 0x74, 0x5d, 0x3d, 0x65, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x59, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x74, 0x29, 0x7b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x67, 0x28, 0x22, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x3a, 0x20, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x58, 0x28, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5a, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x6e, 0x2c, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x31, 0x29, 0x7d, 0x3b, 0x58, 0x28, 0x74, 0x29, 0x2c, 0x64, 0x28, 0x22, 0x45, 0x6a, 0x22, 0x2b, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x74, 0x29, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x29, 0x3b, 0x65, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x3d, 0x6e, 0x2c, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x65, 0x29, 0x2c, 0x74, 0x26, 0x26, 0x5a, 0x28, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x74, 0x72, 0x79, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x22, 0x29, 0x3b, 0x74, 0x2e, 0x73, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x63, 0x73, 0x73, 0x22, 0x29, 0x2c, 0x74, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x53, 0x68, 0x65, 0x65, 0x74, 0x3f, 0x74, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x53, 0x68, 0x65, 0x65, 0x74, 0x2e, 0x63, 0x73, 0x73, 0x54, 0x65, 0x78, 0x74, 0x3d, 0x6e, 0x3a, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x78, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x6e, 0x29, 0x29, 0x2c, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x7c, 0x7c, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x54, 0x61, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x28, 0x22, 0x68, 0x65, 0x61, 0x64, 0x22, 0x29, 0x5b, 0x30, 0x5d, 0x29, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x74, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x6e, 0x29, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6e, 0x28, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x3d, 0x21, 0x30, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3b, 0x69, 0x66, 0x28, 0x21, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x29, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x22, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x5b, 0x5d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4f, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x62, 0x79, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3a, 0x22, 0x2b, 0x6e, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x3b, 0x65, 0x3d, 0x74, 0x2c, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x6e, 0x28, 0x65, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x2c, 0x74, 0x26, 0x26, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x28, 0x74, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x72, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x22, 0x2b, 0x6e, 0x29, 0x2c, 0x7b, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6e, 0x29, 0x7d, 0x2c, 0x67, 0x65, 0x74, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x7d, 0x2c, 0x73, 0x65, 0x74, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x65, 0x3d, 0x74, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x62, 0x79, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x3a, 0x22, 0x2b, 0x6e, 0x2c, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x65, 0x29, 0x29, 0x2c, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x6e, 0x28, 0x65, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x28, 0x65, 0x29, 0x3b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x28, 0x74, 0x29, 0x7d, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x54, 0x49, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x28, 0x6f, 0x6e, 0x3d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x7c, 0x7c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x31, 0x3b, 0x74, 0x3c, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x74, 0x2b, 0x2b, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5b, 0x74, 0x5d, 0x3b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x65, 0x29, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x65, 0x2c, 0x72, 0x29, 0x26, 0x26, 0x28, 0x6e, 0x5b, 0x72, 0x5d, 0x3d, 0x65, 0x5b, 0x72, 0x5d, 0x29, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x7d, 0x29, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x7d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x7b, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3d, 0x7b, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x6c, 0x2c, 0x4c, 0x6f, 0x67, 0x3a, 0x72, 0x2c, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x3a, 0x69, 0x2c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x3a, 0x6f, 0x2c, 0x54, 0x72, 0x61, 0x79, 0x3a, 0x63, 0x2c, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x3a, 0x61, 0x2c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x7b, 0x4f, 0x6e, 0x3a, 0x4b, 0x2c, 0x4f, 0x6e, 0x63, 0x65, 0x3a, 0x51, 0x2c, 0x4f, 0x6e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x3a, 0x56, 0x2c, 0x45, 0x6d, 0x69, 0x74, 0x3a, 0x5a, 0x7d, 0x2c, 0x5f, 0x3a, 0x7b, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x57, 0x2c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x3a, 0x59, 0x2c, 0x41, 0x64, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3a, 0x24, 0x2c, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x53, 0x53, 0x3a, 0x6e, 0x6e, 0x2c, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x3a, 0x74, 0x6e, 0x2c, 0x41, 0x64, 0x64, 0x49, 0x50, 0x43, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3a, 0x66, 0x2c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x4d, 0x2c, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x64, 0x7d, 0x2c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3d, 0x7b, 0x49, 0x73, 0x44, 0x61, 0x72, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x69, 0x73, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x2c, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x6c, 0x6f, 0x67, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x29, 0x2c, 0x41, 0x70, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x61, 0x70, 0x70, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x29, 0x7d, 0x2c, 0x6f, 0x6e, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x6c, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x26, 0x26, 0x21, 0x74, 0x2e, 0x68, 0x61, 0x73, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x6e, 0x6f, 0x2d, 0x64, 0x72, 0x61, 0x67, 0x22, 0x29, 0x3b, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x74, 0x2e, 0x68, 0x61, 0x73, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x64, 0x72, 0x61, 0x67, 0x22, 0x29, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x72, 0x61, 0x67, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x7d, 0x74, 0x3d, 0x74, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x7d, 0x7d, 0x29, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x6d, 0x65, 0x6e, 0x75, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x2c, 0x65, 0x3d, 0x6e, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x26, 0x26, 0x6e, 0x75, 0x6c, 0x6c, 0x3d, 0x3d, 0x28, 0x74, 0x3d, 0x65, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5b, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x6d, 0x65, 0x6e, 0x75, 0x2d, 0x69, 0x64, 0x22, 0x5d, 0x29, 0x3b, 0x29, 0x65, 0x3d, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3b, 0x69, 0x66, 0x28, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x7c, 0x7c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x29, 0x26, 0x26, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x7b, 0x69, 0x64, 0x3a, 0x74, 0x2c, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x65, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5b, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x6d, 0x65, 0x6e, 0x75, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x7c, 0x7c, 0x22, 0x22, 0x7d, 0x3b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x72, 0x29, 0x29, 0x7d, 0x7d, 0x29, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x26, 0x26, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x74, 0x72, 0x79, 0x7b, 0x6e, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6e, 0x29, 0x7d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x5b, 0x74, 0x5d, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x65, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x5b, 0x6e, 0x5d, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x30, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x43, 0x28, 0x5b, 0x74, 0x2c, 0x65, 0x2c, 0x6e, 0x5d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x2c, 0x69, 0x2c, 0x72, 0x29, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x2e, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x3d, 0x6e, 0x7d, 0x2c, 0x69, 0x2e, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x7d, 0x2c, 0x69, 0x7d, 0x28, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x22, 0x29, 0x7d, 0x5d, 0x29, 0x3b, 0x00}; \ No newline at end of file diff --git a/v2/internal/ffenestri/runtime_linux.c b/v2/internal/ffenestri/runtime_linux.c new file mode 100644 index 000000000..a8eb668be --- /dev/null +++ b/v2/internal/ffenestri/runtime_linux.c @@ -0,0 +1,5 @@ + +// runtime.c (c) 2019-Present Lea Anthony. +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file was auto-generated. DO NOT MODIFY. +const unsigned char runtime[]={0x76, 0x61, 0x72, 0x20, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x28, 0x72, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x74, 0x5b, 0x72, 0x5d, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x5b, 0x72, 0x5d, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x74, 0x5b, 0x72, 0x5d, 0x3d, 0x7b, 0x69, 0x3a, 0x72, 0x2c, 0x6c, 0x3a, 0x21, 0x31, 0x2c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x3a, 0x7b, 0x7d, 0x7d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x5b, 0x72, 0x5d, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2c, 0x69, 0x2c, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2c, 0x65, 0x29, 0x2c, 0x69, 0x2e, 0x6c, 0x3d, 0x21, 0x30, 0x2c, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x2e, 0x6d, 0x3d, 0x6e, 0x2c, 0x65, 0x2e, 0x63, 0x3d, 0x74, 0x2c, 0x65, 0x2e, 0x64, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x72, 0x29, 0x7b, 0x65, 0x2e, 0x6f, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7c, 0x7c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x7b, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x21, 0x30, 0x2c, 0x67, 0x65, 0x74, 0x3a, 0x72, 0x7d, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x72, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x22, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x22, 0x21, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x26, 0x26, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x67, 0x26, 0x26, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x67, 0x2c, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x22, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x7d, 0x29, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x22, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x2c, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x21, 0x30, 0x7d, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x31, 0x26, 0x74, 0x26, 0x26, 0x28, 0x6e, 0x3d, 0x65, 0x28, 0x6e, 0x29, 0x29, 0x2c, 0x38, 0x26, 0x74, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x3b, 0x69, 0x66, 0x28, 0x34, 0x26, 0x74, 0x26, 0x26, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x3d, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6e, 0x26, 0x26, 0x6e, 0x26, 0x26, 0x6e, 0x2e, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x69, 0x66, 0x28, 0x65, 0x2e, 0x72, 0x28, 0x72, 0x29, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x72, 0x2c, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x2c, 0x7b, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x21, 0x30, 0x2c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x6e, 0x7d, 0x29, 0x2c, 0x32, 0x26, 0x74, 0x26, 0x26, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x21, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6e, 0x29, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x69, 0x6e, 0x20, 0x6e, 0x29, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x69, 0x2c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x5b, 0x74, 0x5d, 0x7d, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x69, 0x29, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x7d, 0x2c, 0x65, 0x2e, 0x6e, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x26, 0x26, 0x6e, 0x2e, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x3f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x7d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x7d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x2e, 0x64, 0x28, 0x74, 0x2c, 0x22, 0x61, 0x22, 0x2c, 0x74, 0x29, 0x2c, 0x74, 0x7d, 0x2c, 0x65, 0x2e, 0x6f, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x70, 0x3d, 0x22, 0x22, 0x2c, 0x65, 0x28, 0x65, 0x2e, 0x73, 0x3d, 0x30, 0x29, 0x7d, 0x28, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x22, 0x75, 0x73, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x22, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x74, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x72, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x54, 0x72, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x44, 0x65, 0x62, 0x75, 0x67, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x79, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x46, 0x61, 0x74, 0x61, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x68, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x53, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x45, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x69, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x69, 0x2c, 0x22, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x6f, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4e, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x54, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x46, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6a, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x44, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x49, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x50, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x48, 0x69, 0x64, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x41, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x68, 0x6f, 0x77, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4a, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4c, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x46, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x55, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x61, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x61, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x42, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x53, 0x61, 0x76, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x48, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x47, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x75, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x75, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x75, 0x2c, 0x22, 0x4e, 0x65, 0x77, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x6e, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x63, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x63, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x63, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x6e, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x3d, 0x7b, 0x41, 0x70, 0x70, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x22, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x22, 0x2c, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x22, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x22, 0x7d, 0x7d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x73, 0x3d, 0x5b, 0x5d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x28, 0x6e, 0x29, 0x7b, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x28, 0x6e, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x28, 0x6e, 0x29, 0x2c, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3e, 0x30, 0x29, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x30, 0x3b, 0x74, 0x3c, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x74, 0x2b, 0x2b, 0x29, 0x73, 0x5b, 0x74, 0x5d, 0x28, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x4c, 0x22, 0x2b, 0x6e, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x54, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x50, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x79, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x44, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x49, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x57, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x45, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x46, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x53, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x76, 0x61, 0x72, 0x20, 0x4f, 0x2c, 0x45, 0x3d, 0x7b, 0x54, 0x52, 0x41, 0x43, 0x45, 0x3a, 0x31, 0x2c, 0x44, 0x45, 0x42, 0x55, 0x47, 0x3a, 0x32, 0x2c, 0x49, 0x4e, 0x46, 0x4f, 0x3a, 0x33, 0x2c, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x3a, 0x34, 0x2c, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x3a, 0x35, 0x7d, 0x2c, 0x6b, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x26, 0x26, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x7c, 0x7c, 0x28, 0x65, 0x3d, 0x30, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x50, 0x72, 0x6f, 0x6d, 0x69, 0x73, 0x65, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x2c, 0x69, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3b, 0x64, 0x6f, 0x7b, 0x6f, 0x3d, 0x6e, 0x2b, 0x22, 0x2d, 0x22, 0x2b, 0x4f, 0x28, 0x29, 0x7d, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x6b, 0x5b, 0x6f, 0x5d, 0x29, 0x3b, 0x69, 0x66, 0x28, 0x65, 0x3e, 0x30, 0x29, 0x76, 0x61, 0x72, 0x20, 0x61, 0x3d, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x69, 0x28, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x43, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x49, 0x44, 0x3a, 0x20, 0x22, 0x2b, 0x6f, 0x29, 0x29, 0x7d, 0x29, 0x2c, 0x65, 0x29, 0x3b, 0x6b, 0x5b, 0x6f, 0x5d, 0x3d, 0x7b, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3a, 0x61, 0x2c, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x69, 0x2c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x3a, 0x72, 0x7d, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x75, 0x3d, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x6e, 0x2c, 0x61, 0x72, 0x67, 0x73, 0x3a, 0x74, 0x2c, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x44, 0x3a, 0x6f, 0x7d, 0x3b, 0x64, 0x28, 0x22, 0x43, 0x22, 0x2b, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x75, 0x29, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6e, 0x29, 0x7d, 0x7d, 0x29, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x57, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x22, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x20, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x74, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2c, 0x22, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x22, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x6e, 0x29, 0x3b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x79, 0x28, 0x65, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x29, 0x7d, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x69, 0x64, 0x2c, 0x69, 0x3d, 0x6b, 0x5b, 0x72, 0x5d, 0x3b, 0x69, 0x66, 0x28, 0x21, 0x69, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3d, 0x22, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x20, 0x27, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x72, 0x2c, 0x22, 0x27, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x21, 0x21, 0x21, 0x22, 0x29, 0x3b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6f, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6f, 0x29, 0x7d, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x69, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x2c, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x6b, 0x5b, 0x72, 0x5d, 0x2c, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3f, 0x69, 0x2e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3a, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x28, 0x74, 0x2e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x78, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x31, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x43, 0x28, 0x22, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x22, 0x2b, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4d, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x28, 0x22, 0x52, 0x42, 0x4f, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x63, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, 0x28, 0x6e, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x54, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6a, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x46, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x66, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x73, 0x3a, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x3a, 0x22, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x70, 0x3a, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x3a, 0x22, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x48, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4a, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x53, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4c, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x4d, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x52, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x55, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x6d, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x75, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x55, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x43, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x42, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x78, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x48, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x78, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x78, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x4f, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x3f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x3d, 0x6e, 0x65, 0x77, 0x20, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x31, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x67, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x28, 0x6e, 0x29, 0x5b, 0x30, 0x5d, 0x7d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x39, 0x30, 0x30, 0x37, 0x31, 0x39, 0x39, 0x32, 0x35, 0x34, 0x37, 0x34, 0x30, 0x39, 0x39, 0x31, 0x2a, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x29, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x7b, 0x7d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x71, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x28, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x21, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x21, 0x28, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x74, 0x29, 0x29, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x43, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x7d, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x6e, 0x29, 0x2c, 0x65, 0x3d, 0x65, 0x7c, 0x7c, 0x2d, 0x31, 0x2c, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x29, 0x2c, 0x2d, 0x31, 0x21, 0x3d, 0x3d, 0x65, 0x26, 0x26, 0x30, 0x3d, 0x3d, 0x3d, 0x28, 0x65, 0x2d, 0x3d, 0x31, 0x29, 0x7d, 0x7d, 0x2c, 0x7a, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x7a, 0x5b, 0x6e, 0x5d, 0x3d, 0x7a, 0x5b, 0x6e, 0x5d, 0x7c, 0x7c, 0x5b, 0x5d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x6e, 0x65, 0x77, 0x20, 0x71, 0x28, 0x74, 0x2c, 0x65, 0x29, 0x3b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x22, 0x50, 0x75, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3a, 0x20, 0x22, 0x2b, 0x6e, 0x29, 0x2c, 0x7a, 0x5b, 0x6e, 0x5d, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x72, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4b, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x51, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x31, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x58, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x69, 0x66, 0x28, 0x7a, 0x5b, 0x74, 0x5d, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x7a, 0x5b, 0x74, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x29, 0x2c, 0x72, 0x3d, 0x30, 0x3b, 0x72, 0x3c, 0x7a, 0x5b, 0x74, 0x5d, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x72, 0x2b, 0x3d, 0x31, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x7a, 0x5b, 0x74, 0x5d, 0x5b, 0x72, 0x5d, 0x2c, 0x6f, 0x3d, 0x6e, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3b, 0x69, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x6f, 0x29, 0x26, 0x26, 0x65, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x72, 0x2c, 0x31, 0x29, 0x7d, 0x7a, 0x5b, 0x74, 0x5d, 0x3d, 0x65, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x59, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x74, 0x29, 0x7b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x67, 0x28, 0x22, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x3a, 0x20, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x58, 0x28, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5a, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x6e, 0x2c, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x31, 0x29, 0x7d, 0x3b, 0x58, 0x28, 0x74, 0x29, 0x2c, 0x64, 0x28, 0x22, 0x45, 0x6a, 0x22, 0x2b, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x74, 0x29, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x29, 0x3b, 0x65, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x3d, 0x6e, 0x2c, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x65, 0x29, 0x2c, 0x74, 0x26, 0x26, 0x5a, 0x28, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x74, 0x72, 0x79, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x22, 0x29, 0x3b, 0x74, 0x2e, 0x73, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x63, 0x73, 0x73, 0x22, 0x29, 0x2c, 0x74, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x53, 0x68, 0x65, 0x65, 0x74, 0x3f, 0x74, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x53, 0x68, 0x65, 0x65, 0x74, 0x2e, 0x63, 0x73, 0x73, 0x54, 0x65, 0x78, 0x74, 0x3d, 0x6e, 0x3a, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x78, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x6e, 0x29, 0x29, 0x2c, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x7c, 0x7c, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x54, 0x61, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x28, 0x22, 0x68, 0x65, 0x61, 0x64, 0x22, 0x29, 0x5b, 0x30, 0x5d, 0x29, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x74, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x6e, 0x29, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6e, 0x28, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x3d, 0x21, 0x30, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3b, 0x69, 0x66, 0x28, 0x21, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x29, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x22, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x5b, 0x5d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4f, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x62, 0x79, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3a, 0x22, 0x2b, 0x6e, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x3b, 0x65, 0x3d, 0x74, 0x2c, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x6e, 0x28, 0x65, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x2c, 0x74, 0x26, 0x26, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x28, 0x74, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x72, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x22, 0x2b, 0x6e, 0x29, 0x2c, 0x7b, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6e, 0x29, 0x7d, 0x2c, 0x67, 0x65, 0x74, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x7d, 0x2c, 0x73, 0x65, 0x74, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x65, 0x3d, 0x74, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x62, 0x79, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x3a, 0x22, 0x2b, 0x6e, 0x2c, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x65, 0x29, 0x29, 0x2c, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x6e, 0x28, 0x65, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x28, 0x65, 0x29, 0x3b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x28, 0x74, 0x29, 0x7d, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x54, 0x49, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x28, 0x6f, 0x6e, 0x3d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x7c, 0x7c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x31, 0x3b, 0x74, 0x3c, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x74, 0x2b, 0x2b, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5b, 0x74, 0x5d, 0x3b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x65, 0x29, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x65, 0x2c, 0x72, 0x29, 0x26, 0x26, 0x28, 0x6e, 0x5b, 0x72, 0x5d, 0x3d, 0x65, 0x5b, 0x72, 0x5d, 0x29, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x7d, 0x29, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x7d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x7b, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3d, 0x7b, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x6c, 0x2c, 0x4c, 0x6f, 0x67, 0x3a, 0x72, 0x2c, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x3a, 0x69, 0x2c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x3a, 0x6f, 0x2c, 0x54, 0x72, 0x61, 0x79, 0x3a, 0x63, 0x2c, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x3a, 0x61, 0x2c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x7b, 0x4f, 0x6e, 0x3a, 0x4b, 0x2c, 0x4f, 0x6e, 0x63, 0x65, 0x3a, 0x51, 0x2c, 0x4f, 0x6e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x3a, 0x56, 0x2c, 0x45, 0x6d, 0x69, 0x74, 0x3a, 0x5a, 0x7d, 0x2c, 0x5f, 0x3a, 0x7b, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x57, 0x2c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x3a, 0x59, 0x2c, 0x41, 0x64, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3a, 0x24, 0x2c, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x53, 0x53, 0x3a, 0x6e, 0x6e, 0x2c, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x3a, 0x74, 0x6e, 0x2c, 0x41, 0x64, 0x64, 0x49, 0x50, 0x43, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3a, 0x66, 0x2c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x78, 0x2c, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x64, 0x7d, 0x2c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3d, 0x7b, 0x49, 0x73, 0x44, 0x61, 0x72, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x69, 0x73, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x2c, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x6c, 0x6f, 0x67, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x29, 0x2c, 0x41, 0x70, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x61, 0x70, 0x70, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x29, 0x7d, 0x2c, 0x6f, 0x6e, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x6c, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x26, 0x26, 0x21, 0x74, 0x2e, 0x68, 0x61, 0x73, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x6e, 0x6f, 0x2d, 0x64, 0x72, 0x61, 0x67, 0x22, 0x29, 0x3b, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x74, 0x2e, 0x68, 0x61, 0x73, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x64, 0x72, 0x61, 0x67, 0x22, 0x29, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x72, 0x61, 0x67, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x7d, 0x74, 0x3d, 0x74, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x7d, 0x7d, 0x29, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x6d, 0x65, 0x6e, 0x75, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x2c, 0x65, 0x3d, 0x6e, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x26, 0x26, 0x6e, 0x75, 0x6c, 0x6c, 0x3d, 0x3d, 0x28, 0x74, 0x3d, 0x65, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5b, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x6d, 0x65, 0x6e, 0x75, 0x2d, 0x69, 0x64, 0x22, 0x5d, 0x29, 0x3b, 0x29, 0x65, 0x3d, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3b, 0x69, 0x66, 0x28, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x7c, 0x7c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x29, 0x26, 0x26, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x7b, 0x69, 0x64, 0x3a, 0x74, 0x2c, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x65, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5b, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x6d, 0x65, 0x6e, 0x75, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x7c, 0x7c, 0x22, 0x22, 0x7d, 0x3b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x72, 0x29, 0x29, 0x7d, 0x7d, 0x29, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x26, 0x26, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x74, 0x72, 0x79, 0x7b, 0x6e, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6e, 0x29, 0x7d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x5b, 0x74, 0x5d, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x65, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x5b, 0x6e, 0x5d, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x30, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x43, 0x28, 0x5b, 0x74, 0x2c, 0x65, 0x2c, 0x6e, 0x5d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x2c, 0x69, 0x2c, 0x72, 0x29, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x2e, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x3d, 0x6e, 0x7d, 0x2c, 0x69, 0x2e, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x7d, 0x2c, 0x69, 0x7d, 0x28, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x22, 0x29, 0x7d, 0x5d, 0x29, 0x3b, 0x00}; \ No newline at end of file diff --git a/v2/internal/ffenestri/runtime_windows.c b/v2/internal/ffenestri/runtime_windows.c new file mode 100644 index 000000000..e9a3ff705 --- /dev/null +++ b/v2/internal/ffenestri/runtime_windows.c @@ -0,0 +1,5 @@ + +// runtime.c (c) 2019-Present Lea Anthony. +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file was auto-generated. DO NOT MODIFY. +const unsigned char runtime[]={0x76, 0x61, 0x72, 0x20, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x28, 0x72, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x74, 0x5b, 0x72, 0x5d, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x5b, 0x72, 0x5d, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x74, 0x5b, 0x72, 0x5d, 0x3d, 0x7b, 0x69, 0x3a, 0x72, 0x2c, 0x6c, 0x3a, 0x21, 0x31, 0x2c, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x3a, 0x7b, 0x7d, 0x7d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x5b, 0x72, 0x5d, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2c, 0x69, 0x2c, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x2c, 0x65, 0x29, 0x2c, 0x69, 0x2e, 0x6c, 0x3d, 0x21, 0x30, 0x2c, 0x69, 0x2e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x2e, 0x6d, 0x3d, 0x6e, 0x2c, 0x65, 0x2e, 0x63, 0x3d, 0x74, 0x2c, 0x65, 0x2e, 0x64, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x72, 0x29, 0x7b, 0x65, 0x2e, 0x6f, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7c, 0x7c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x7b, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x21, 0x30, 0x2c, 0x67, 0x65, 0x74, 0x3a, 0x72, 0x7d, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x72, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x22, 0x75, 0x6e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x22, 0x21, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x26, 0x26, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x67, 0x26, 0x26, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x2e, 0x74, 0x6f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x67, 0x2c, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x22, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x7d, 0x29, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x6e, 0x2c, 0x22, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x2c, 0x7b, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x21, 0x30, 0x7d, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x31, 0x26, 0x74, 0x26, 0x26, 0x28, 0x6e, 0x3d, 0x65, 0x28, 0x6e, 0x29, 0x29, 0x2c, 0x38, 0x26, 0x74, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x3b, 0x69, 0x66, 0x28, 0x34, 0x26, 0x74, 0x26, 0x26, 0x22, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x3d, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6e, 0x26, 0x26, 0x6e, 0x26, 0x26, 0x6e, 0x2e, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x29, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x3b, 0x69, 0x66, 0x28, 0x65, 0x2e, 0x72, 0x28, 0x72, 0x29, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x28, 0x72, 0x2c, 0x22, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x2c, 0x7b, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x21, 0x30, 0x2c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x6e, 0x7d, 0x29, 0x2c, 0x32, 0x26, 0x74, 0x26, 0x26, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x21, 0x3d, 0x74, 0x79, 0x70, 0x65, 0x6f, 0x66, 0x20, 0x6e, 0x29, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x69, 0x20, 0x69, 0x6e, 0x20, 0x6e, 0x29, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x69, 0x2c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x5b, 0x74, 0x5d, 0x7d, 0x2e, 0x62, 0x69, 0x6e, 0x64, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x69, 0x29, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x7d, 0x2c, 0x65, 0x2e, 0x6e, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x26, 0x26, 0x6e, 0x2e, 0x5f, 0x5f, 0x65, 0x73, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x3f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x2e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x7d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x7d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x2e, 0x64, 0x28, 0x74, 0x2c, 0x22, 0x61, 0x22, 0x2c, 0x74, 0x29, 0x2c, 0x74, 0x7d, 0x2c, 0x65, 0x2e, 0x6f, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x2c, 0x65, 0x2e, 0x70, 0x3d, 0x22, 0x22, 0x2c, 0x65, 0x28, 0x65, 0x2e, 0x73, 0x3d, 0x30, 0x29, 0x7d, 0x28, 0x5b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x22, 0x75, 0x73, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x22, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x74, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x72, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x54, 0x72, 0x61, 0x63, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x76, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x70, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x44, 0x65, 0x62, 0x75, 0x67, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x79, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6d, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x67, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x46, 0x61, 0x74, 0x61, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x68, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x53, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x72, 0x2c, 0x22, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x45, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x69, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x69, 0x2c, 0x22, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x54, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x6f, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6a, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x78, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x46, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4d, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x46, 0x75, 0x6c, 0x6c, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x49, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x44, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x50, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x48, 0x69, 0x64, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x41, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x53, 0x68, 0x6f, 0x77, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4a, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4c, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x52, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x55, 0x6e, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x69, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x46, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x6f, 0x2c, 0x22, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x55, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x61, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x61, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x42, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x53, 0x61, 0x76, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x48, 0x7d, 0x29, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x61, 0x2c, 0x22, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x47, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x75, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x75, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x75, 0x2c, 0x22, 0x4e, 0x65, 0x77, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x6e, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x63, 0x3d, 0x7b, 0x7d, 0x3b, 0x65, 0x2e, 0x72, 0x28, 0x63, 0x29, 0x2c, 0x65, 0x2e, 0x64, 0x28, 0x63, 0x2c, 0x22, 0x53, 0x65, 0x74, 0x49, 0x63, 0x6f, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x6e, 0x7d, 0x29, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x6c, 0x3d, 0x7b, 0x41, 0x70, 0x70, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x22, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x22, 0x2c, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x22, 0x7d, 0x7d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x73, 0x3d, 0x5b, 0x5d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x28, 0x6e, 0x29, 0x7b, 0x73, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x28, 0x6e, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x28, 0x6e, 0x29, 0x2c, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3e, 0x30, 0x29, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x30, 0x3b, 0x74, 0x3c, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x74, 0x2b, 0x2b, 0x29, 0x73, 0x5b, 0x74, 0x5d, 0x28, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x4c, 0x22, 0x2b, 0x6e, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x54, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x50, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x79, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x44, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x49, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x62, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x57, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x45, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x46, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x28, 0x22, 0x53, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x76, 0x61, 0x72, 0x20, 0x4f, 0x2c, 0x45, 0x3d, 0x7b, 0x54, 0x52, 0x41, 0x43, 0x45, 0x3a, 0x31, 0x2c, 0x44, 0x45, 0x42, 0x55, 0x47, 0x3a, 0x32, 0x2c, 0x49, 0x4e, 0x46, 0x4f, 0x3a, 0x33, 0x2c, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x3a, 0x34, 0x2c, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x3a, 0x35, 0x7d, 0x2c, 0x6b, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x26, 0x26, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x7c, 0x7c, 0x28, 0x65, 0x3d, 0x30, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x50, 0x72, 0x6f, 0x6d, 0x69, 0x73, 0x65, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x72, 0x2c, 0x69, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3b, 0x64, 0x6f, 0x7b, 0x6f, 0x3d, 0x6e, 0x2b, 0x22, 0x2d, 0x22, 0x2b, 0x4f, 0x28, 0x29, 0x7d, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x6b, 0x5b, 0x6f, 0x5d, 0x29, 0x3b, 0x69, 0x66, 0x28, 0x65, 0x3e, 0x30, 0x29, 0x76, 0x61, 0x72, 0x20, 0x61, 0x3d, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x69, 0x28, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x43, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x6f, 0x20, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x49, 0x44, 0x3a, 0x20, 0x22, 0x2b, 0x6f, 0x29, 0x29, 0x7d, 0x29, 0x2c, 0x65, 0x29, 0x3b, 0x6b, 0x5b, 0x6f, 0x5d, 0x3d, 0x7b, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3a, 0x61, 0x2c, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x69, 0x2c, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x3a, 0x72, 0x7d, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x75, 0x3d, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x6e, 0x2c, 0x61, 0x72, 0x67, 0x73, 0x3a, 0x74, 0x2c, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x44, 0x3a, 0x6f, 0x7d, 0x3b, 0x64, 0x28, 0x22, 0x43, 0x22, 0x2b, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x75, 0x29, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6e, 0x29, 0x7d, 0x7d, 0x29, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x57, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x22, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x20, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x74, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2c, 0x22, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x22, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x6e, 0x29, 0x3b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x79, 0x28, 0x65, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x29, 0x7d, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x74, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x69, 0x64, 0x2c, 0x69, 0x3d, 0x6b, 0x5b, 0x72, 0x5d, 0x3b, 0x69, 0x66, 0x28, 0x21, 0x69, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6f, 0x3d, 0x22, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x20, 0x27, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x28, 0x72, 0x2c, 0x22, 0x27, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x21, 0x21, 0x21, 0x22, 0x29, 0x3b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6f, 0x29, 0x2c, 0x6e, 0x65, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6f, 0x29, 0x7d, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x28, 0x69, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x2c, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x6b, 0x5b, 0x72, 0x5d, 0x2c, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x3f, 0x69, 0x2e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x74, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x29, 0x3a, 0x69, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x28, 0x74, 0x2e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x31, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x43, 0x28, 0x22, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x22, 0x2b, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x54, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x28, 0x22, 0x52, 0x42, 0x4f, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6a, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x63, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x78, 0x28, 0x6e, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x54, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4d, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x46, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x66, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x73, 0x3a, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x3a, 0x22, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x70, 0x3a, 0x22, 0x2b, 0x6e, 0x2b, 0x22, 0x3a, 0x22, 0x2b, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x48, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4a, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x53, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4c, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x4d, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x52, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x55, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5f, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x6d, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x46, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x75, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x55, 0x28, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x57, 0x43, 0x22, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x42, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4e, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x48, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4e, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x4e, 0x28, 0x22, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x2c, 0x6e, 0x29, 0x7d, 0x4f, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x3f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x6e, 0x3d, 0x6e, 0x65, 0x77, 0x20, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x72, 0x61, 0x79, 0x28, 0x31, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x67, 0x65, 0x74, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x28, 0x6e, 0x29, 0x5b, 0x30, 0x5d, 0x7d, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x39, 0x30, 0x30, 0x37, 0x31, 0x39, 0x39, 0x32, 0x35, 0x34, 0x37, 0x34, 0x30, 0x39, 0x39, 0x31, 0x2a, 0x4d, 0x61, 0x74, 0x68, 0x2e, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x28, 0x29, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x7b, 0x7d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x71, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x28, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x21, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x21, 0x28, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x6f, 0x66, 0x20, 0x74, 0x29, 0x29, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x54, 0x79, 0x70, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x43, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x61, 0x73, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x29, 0x7d, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x6e, 0x29, 0x2c, 0x65, 0x3d, 0x65, 0x7c, 0x7c, 0x2d, 0x31, 0x2c, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x2c, 0x6e, 0x29, 0x2c, 0x2d, 0x31, 0x21, 0x3d, 0x3d, 0x65, 0x26, 0x26, 0x30, 0x3d, 0x3d, 0x3d, 0x28, 0x65, 0x2d, 0x3d, 0x31, 0x29, 0x7d, 0x7d, 0x2c, 0x7a, 0x3d, 0x7b, 0x7d, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x65, 0x29, 0x7b, 0x7a, 0x5b, 0x6e, 0x5d, 0x3d, 0x7a, 0x5b, 0x6e, 0x5d, 0x7c, 0x7c, 0x5b, 0x5d, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x6e, 0x65, 0x77, 0x20, 0x71, 0x28, 0x74, 0x2c, 0x65, 0x29, 0x3b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x22, 0x50, 0x75, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3a, 0x20, 0x22, 0x2b, 0x6e, 0x29, 0x2c, 0x7a, 0x5b, 0x6e, 0x5d, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x72, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4b, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x51, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x56, 0x28, 0x6e, 0x2c, 0x74, 0x2c, 0x31, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x58, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3b, 0x69, 0x66, 0x28, 0x7a, 0x5b, 0x74, 0x5d, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x7a, 0x5b, 0x74, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x29, 0x2c, 0x72, 0x3d, 0x30, 0x3b, 0x72, 0x3c, 0x7a, 0x5b, 0x74, 0x5d, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x72, 0x2b, 0x3d, 0x31, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x7a, 0x5b, 0x74, 0x5d, 0x5b, 0x72, 0x5d, 0x2c, 0x6f, 0x3d, 0x6e, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x3b, 0x69, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x28, 0x6f, 0x29, 0x26, 0x26, 0x65, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x72, 0x2c, 0x31, 0x29, 0x7d, 0x7a, 0x5b, 0x74, 0x5d, 0x3d, 0x65, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x59, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3b, 0x74, 0x72, 0x79, 0x7b, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x74, 0x29, 0x7b, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x67, 0x28, 0x22, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x3a, 0x20, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x58, 0x28, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5a, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x6e, 0x2c, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x28, 0x31, 0x29, 0x7d, 0x3b, 0x58, 0x28, 0x74, 0x29, 0x2c, 0x64, 0x28, 0x22, 0x45, 0x6a, 0x22, 0x2b, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x74, 0x29, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x24, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x29, 0x3b, 0x65, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x3d, 0x6e, 0x2c, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x62, 0x6f, 0x64, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x65, 0x29, 0x2c, 0x74, 0x26, 0x26, 0x5a, 0x28, 0x74, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x74, 0x72, 0x79, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x28, 0x22, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x22, 0x29, 0x3b, 0x74, 0x2e, 0x73, 0x65, 0x74, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x63, 0x73, 0x73, 0x22, 0x29, 0x2c, 0x74, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x53, 0x68, 0x65, 0x65, 0x74, 0x3f, 0x74, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x53, 0x68, 0x65, 0x65, 0x74, 0x2e, 0x63, 0x73, 0x73, 0x54, 0x65, 0x78, 0x74, 0x3d, 0x6e, 0x3a, 0x74, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x78, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x28, 0x6e, 0x29, 0x29, 0x2c, 0x28, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x7c, 0x7c, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x54, 0x61, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x28, 0x22, 0x68, 0x65, 0x61, 0x64, 0x22, 0x29, 0x5b, 0x30, 0x5d, 0x29, 0x2e, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x28, 0x74, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x67, 0x28, 0x6e, 0x29, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6e, 0x28, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x3d, 0x21, 0x30, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x6e, 0x28, 0x6e, 0x2c, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3b, 0x69, 0x66, 0x28, 0x21, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x29, 0x74, 0x68, 0x72, 0x6f, 0x77, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x22, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x73, 0x65, 0x64, 0x22, 0x29, 0x3b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x5b, 0x5d, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4f, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x62, 0x79, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3a, 0x22, 0x2b, 0x6e, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x3b, 0x65, 0x3d, 0x74, 0x2c, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x6e, 0x28, 0x65, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x2c, 0x74, 0x26, 0x26, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x28, 0x74, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x72, 0x65, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x22, 0x2b, 0x6e, 0x29, 0x2c, 0x7b, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x2e, 0x70, 0x75, 0x73, 0x68, 0x28, 0x6e, 0x29, 0x7d, 0x2c, 0x67, 0x65, 0x74, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x65, 0x7d, 0x2c, 0x73, 0x65, 0x74, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x65, 0x3d, 0x74, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x73, 0x79, 0x6e, 0x63, 0x3a, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x62, 0x79, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x3a, 0x22, 0x2b, 0x6e, 0x2c, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x65, 0x29, 0x29, 0x2c, 0x72, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x6e, 0x28, 0x65, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x28, 0x65, 0x29, 0x3b, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x28, 0x74, 0x29, 0x7d, 0x7d, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x64, 0x28, 0x22, 0x54, 0x49, 0x22, 0x2b, 0x6e, 0x29, 0x7d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x28, 0x6f, 0x6e, 0x3d, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x7c, 0x7c, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x31, 0x3b, 0x74, 0x3c, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3b, 0x74, 0x2b, 0x2b, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x65, 0x3d, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x5b, 0x74, 0x5d, 0x3b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x72, 0x20, 0x69, 0x6e, 0x20, 0x65, 0x29, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x68, 0x61, 0x73, 0x4f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x65, 0x2c, 0x72, 0x29, 0x26, 0x26, 0x28, 0x6e, 0x5b, 0x72, 0x5d, 0x3d, 0x65, 0x5b, 0x72, 0x5d, 0x29, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x7d, 0x29, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2c, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x7d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x7b, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3d, 0x7b, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x6c, 0x2c, 0x4c, 0x6f, 0x67, 0x3a, 0x72, 0x2c, 0x42, 0x72, 0x6f, 0x77, 0x73, 0x65, 0x72, 0x3a, 0x69, 0x2c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x3a, 0x6f, 0x2c, 0x54, 0x72, 0x61, 0x79, 0x3a, 0x63, 0x2c, 0x44, 0x69, 0x61, 0x6c, 0x6f, 0x67, 0x3a, 0x61, 0x2c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x7b, 0x4f, 0x6e, 0x3a, 0x4b, 0x2c, 0x4f, 0x6e, 0x63, 0x65, 0x3a, 0x51, 0x2c, 0x4f, 0x6e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x3a, 0x56, 0x2c, 0x45, 0x6d, 0x69, 0x74, 0x3a, 0x5a, 0x7d, 0x2c, 0x5f, 0x3a, 0x7b, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x3a, 0x57, 0x2c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x3a, 0x59, 0x2c, 0x41, 0x64, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x3a, 0x24, 0x2c, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x53, 0x53, 0x3a, 0x6e, 0x6e, 0x2c, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x3a, 0x74, 0x6e, 0x2c, 0x41, 0x64, 0x64, 0x49, 0x50, 0x43, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x3a, 0x66, 0x2c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x4e, 0x2c, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x64, 0x7d, 0x2c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x3a, 0x75, 0x7d, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3d, 0x7b, 0x49, 0x73, 0x44, 0x61, 0x72, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x69, 0x73, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x29, 0x2c, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x6c, 0x6f, 0x67, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x29, 0x2c, 0x41, 0x70, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x65, 0x6e, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x61, 0x70, 0x70, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x29, 0x7d, 0x2c, 0x6f, 0x6e, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2c, 0x6c, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x6d, 0x6f, 0x75, 0x73, 0x65, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x3d, 0x6e, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x26, 0x26, 0x21, 0x74, 0x2e, 0x68, 0x61, 0x73, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x6e, 0x6f, 0x2d, 0x64, 0x72, 0x61, 0x67, 0x22, 0x29, 0x3b, 0x29, 0x7b, 0x69, 0x66, 0x28, 0x74, 0x2e, 0x68, 0x61, 0x73, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, 0x22, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x64, 0x72, 0x61, 0x67, 0x22, 0x29, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x64, 0x72, 0x61, 0x67, 0x22, 0x29, 0x3b, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x7d, 0x74, 0x3d, 0x74, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x7d, 0x7d, 0x29, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x61, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x28, 0x22, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x6d, 0x65, 0x6e, 0x75, 0x22, 0x2c, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x66, 0x6f, 0x72, 0x28, 0x76, 0x61, 0x72, 0x20, 0x74, 0x2c, 0x65, 0x3d, 0x6e, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3b, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x65, 0x26, 0x26, 0x6e, 0x75, 0x6c, 0x6c, 0x3d, 0x3d, 0x28, 0x74, 0x3d, 0x65, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5b, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x6d, 0x65, 0x6e, 0x75, 0x2d, 0x69, 0x64, 0x22, 0x5d, 0x29, 0x3b, 0x29, 0x65, 0x3d, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x3b, 0x69, 0x66, 0x28, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x7c, 0x7c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x57, 0x61, 0x69, 0x6c, 0x73, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x6e, 0x75, 0x29, 0x26, 0x26, 0x6e, 0x2e, 0x70, 0x72, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x28, 0x29, 0x2c, 0x6e, 0x75, 0x6c, 0x6c, 0x21, 0x3d, 0x74, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x7b, 0x69, 0x64, 0x3a, 0x74, 0x2c, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x65, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5b, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2d, 0x6d, 0x65, 0x6e, 0x75, 0x2d, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x7c, 0x7c, 0x22, 0x22, 0x7d, 0x3b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x28, 0x22, 0x43, 0x22, 0x2b, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x69, 0x66, 0x79, 0x28, 0x72, 0x29, 0x29, 0x7d, 0x7d, 0x29, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x26, 0x26, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x74, 0x72, 0x79, 0x7b, 0x6e, 0x3d, 0x4a, 0x53, 0x4f, 0x4e, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x28, 0x6e, 0x29, 0x7d, 0x63, 0x61, 0x74, 0x63, 0x68, 0x28, 0x6e, 0x29, 0x7b, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x6e, 0x29, 0x7d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x5b, 0x74, 0x5d, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x65, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x3d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x7c, 0x7c, 0x7b, 0x7d, 0x2c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6b, 0x65, 0x79, 0x73, 0x28, 0x6e, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x29, 0x2e, 0x66, 0x6f, 0x72, 0x45, 0x61, 0x63, 0x68, 0x28, 0x28, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5b, 0x74, 0x5d, 0x5b, 0x65, 0x5d, 0x5b, 0x6e, 0x5d, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x72, 0x3d, 0x30, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x28, 0x29, 0x7b, 0x76, 0x61, 0x72, 0x20, 0x69, 0x3d, 0x5b, 0x5d, 0x2e, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x61, 0x6c, 0x6c, 0x28, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x29, 0x3b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x43, 0x28, 0x5b, 0x74, 0x2c, 0x65, 0x2c, 0x6e, 0x5d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x2c, 0x69, 0x2c, 0x72, 0x29, 0x7d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x2e, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x29, 0x7b, 0x72, 0x3d, 0x6e, 0x7d, 0x2c, 0x69, 0x2e, 0x67, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3d, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x7b, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x7d, 0x2c, 0x69, 0x7d, 0x28, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x29, 0x29, 0x7d, 0x28, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x29, 0x2c, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x2e, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x69, 0x74, 0x28, 0x22, 0x77, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x22, 0x29, 0x7d, 0x5d, 0x29, 0x3b, 0x00}; \ No newline at end of file diff --git a/v2/internal/ffenestri/traymenu_darwin.c b/v2/internal/ffenestri/traymenu_darwin.c new file mode 100644 index 000000000..1854d838d --- /dev/null +++ b/v2/internal/ffenestri/traymenu_darwin.c @@ -0,0 +1,266 @@ +// +// Created by Lea Anthony on 12/1/21. +// + +#include "common.h" +#include "traymenu_darwin.h" +#include "trayicons.h" + +extern Class trayMenuDelegateClass; + +// A cache for all our tray menu icons +// Global because it's a singleton +struct hashmap_s trayIconCache; + +TrayMenu* NewTrayMenu(const char* menuJSON) { + TrayMenu* result = malloc(sizeof(TrayMenu)); + +/* + {"ID":"0","Label":"Test Tray Label","Icon":"","ProcessedMenu":{"Menu":{"Items":[{"ID":"0","Label":"Show Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0},{"ID":"1","Label":"Hide Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0},{"ID":"2","Label":"Minimise Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0},{"ID":"3","Label":"Unminimise Window","Type":"Text","Disabled":false,"Hidden":false,"Checked":false,"Foreground":0,"Background":0}]},"RadioGroups":null}} +*/ + JsonNode* processedJSON = json_decode(menuJSON); + if( processedJSON == NULL ) { + ABORT("[NewTrayMenu] Unable to parse TrayMenu JSON: %s", menuJSON); + } + + // Save reference to this json + result->processedJSON = processedJSON; + + // TODO: Make this configurable + result->trayIconPosition = NSImageLeft; + + result->ID = mustJSONString(processedJSON, "ID"); + result->label = mustJSONString(processedJSON, "Label"); + result->icon = mustJSONString(processedJSON, "Image"); + result->fontName = getJSONString(processedJSON, "FontName"); + result->RGBA = getJSONString(processedJSON, "RGBA"); + getJSONBool(processedJSON, "MacTemplateImage", &result->templateImage); + result->fontSize = 0; + getJSONInt(processedJSON, "FontSize", &result->fontSize); + result->tooltip = NULL; + result->tooltip = getJSONString(processedJSON, "Tooltip"); + result->disabled = false; + getJSONBool(processedJSON, "Disabled", &result->disabled); + + result->styledLabel = getJSONObject(processedJSON, "StyledLabel"); + + // Create the menu + JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu"); + result->menu = NewMenu(processedMenu); + + result->delegate = NULL; + + // Init tray status bar item + result->statusbaritem = NULL; + + // Set the menu type and store the tray ID in the parent data + result->menu->menuType = TrayMenuType; + result->menu->parentData = (void*) result->ID; + + return result; +} + +void DumpTrayMenu(TrayMenu* trayMenu) { + printf(" ['%s':%p] = { label: '%s', icon: '%s', menu: %p, statusbar: %p }\n", trayMenu->ID, trayMenu, trayMenu->label, trayMenu->icon, trayMenu->menu, trayMenu->statusbaritem ); +} + + +void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel) { + + // Exit early if NULL + if( trayMenu->label == NULL ) { + return; + } + // Update button label + id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button")); + id attributedString = NULL; + if( styledLabel != NULL) { + attributedString = createAttributedStringFromStyledLabel(styledLabel, fontName, fontSize); + } else { + attributedString = createAttributedString(label, fontName, fontSize, RGBA); + } + + if( tooltip != NULL ) { + msg_id(statusBarButton, s("setToolTip:"), str(tooltip)); + } + + msg_bool(statusBarButton, s("setEnabled:"), !disabled); + + msg_id(statusBarButton, s("setAttributedTitle:"), attributedString); +} + +void UpdateTrayIcon(TrayMenu *trayMenu) { + + // Exit early if NULL + if( trayMenu->icon == NULL ) { + return; + } + + id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button")); + + // Empty icon means remove it + if( STREMPTY(trayMenu->icon) ) { + // Remove image + msg_id(statusBarButton, s("setImage:"), NULL); + return; + } + + id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon)); + + // If we don't have the image in the icon cache then assume it's base64 encoded image data + if (trayImage == NULL) { + trayImage = createImageFromBase64Data(trayMenu->icon, trayMenu->templateImage); + } + + msg_int(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition); + msg_id(statusBarButton, s("setImage:"), trayImage); + +} + +void ShowTrayMenu(TrayMenu* trayMenu) { + + // Create a status bar item if we don't have one + if( trayMenu->statusbaritem == NULL ) { + id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") ); + trayMenu->statusbaritem = ((id(*)(id, SEL, CGFloat))objc_msgSend)(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength); + msg_reg(trayMenu->statusbaritem, s("retain")); + } + + id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button")); + msg_uint(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition); + // Update the icon if needed + UpdateTrayIcon(trayMenu); + + // Update the label if needed + UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA, trayMenu->tooltip, trayMenu->disabled, trayMenu->styledLabel); + + // Update the menu + id menu = GetMenu(trayMenu->menu); + objc_setAssociatedObject(menu, "trayMenuID", str(trayMenu->ID), OBJC_ASSOCIATION_ASSIGN); + + // Create delegate + id trayMenuDelegate = msg_reg((id)trayMenuDelegateClass, s("new")); + msg_id(menu, s("setDelegate:"), trayMenuDelegate); + objc_setAssociatedObject(trayMenuDelegate, "menu", menu, OBJC_ASSOCIATION_ASSIGN); + + // Create menu delegate + trayMenu->delegate = trayMenuDelegate; + + msg_id(trayMenu->statusbaritem, s("setMenu:"), menu); +} + +// UpdateTrayMenuInPlace receives 2 menus. The current menu gets +// updated with the data from the new menu. +void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) { + + // Delete the old menu + DeleteMenu(currentMenu->menu); + if( currentMenu->delegate != NULL ) { + msg_reg(currentMenu->delegate, s("release")); + currentMenu->delegate = NULL; + } + + // Set the new one + currentMenu->menu = newMenu->menu; + + // Delete the old JSON + json_delete(currentMenu->processedJSON); + + // Set the new JSON + currentMenu->processedJSON = newMenu->processedJSON; + + // Copy the other data + currentMenu->ID = newMenu->ID; + currentMenu->label = newMenu->label; + currentMenu->styledLabel = newMenu->styledLabel; + currentMenu->trayIconPosition = newMenu->trayIconPosition; + currentMenu->icon = newMenu->icon; + +} + +void DeleteTrayMenu(TrayMenu* trayMenu) { + + // Delete the menu + DeleteMenu(trayMenu->menu); + if( trayMenu->delegate != NULL ) { + msg_reg(trayMenu->delegate, s("release")); + trayMenu->delegate = NULL; + } + + // Free JSON + if (trayMenu->processedJSON != NULL ) { + json_delete(trayMenu->processedJSON); + } + + // Free the status item + if ( trayMenu->statusbaritem != NULL ) { + id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") ); + msg_id(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem); + msg_reg(trayMenu->statusbaritem, s("release")); + trayMenu->statusbaritem = NULL; + } + + // Free the tray menu memory + MEMFREE(trayMenu); +} +void DeleteTrayMenuKeepStatusBarItem(TrayMenu* trayMenu) { + + // Delete the menu + DeleteMenu(trayMenu->menu); + if( trayMenu->delegate != NULL ) { + msg_reg(trayMenu->delegate, s("release")); + trayMenu->delegate = NULL; + } + + // Free JSON + if (trayMenu->processedJSON != NULL ) { + json_delete(trayMenu->processedJSON); + } + + // Free the tray menu memory + MEMFREE(trayMenu); +} + +void LoadTrayIcons() { + + // Allocate the Tray Icons + if( 0 != hashmap_create((const unsigned)4, &trayIconCache)) { + // Couldn't allocate map + ABORT("Not enough memory to allocate trayIconCache!"); + } + + unsigned int count = 0; + while( 1 ) { + const unsigned char *name = trayIcons[count++]; + if( name == 0x00 ) { + break; + } + const unsigned char *lengthAsString = trayIcons[count++]; + if( name == 0x00 ) { + break; + } + const unsigned char *data = trayIcons[count++]; + if( data == 0x00 ) { + break; + } + int length = atoi((const char *)lengthAsString); + + // Create the icon and add to the hashmap + id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), (id)data, length); + id trayImage = ALLOC("NSImage"); + msg_id(trayImage, s("initWithData:"), imageData); + hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage); + } +} + +void UnloadTrayIcons() { + // Release the tray cache images + if( hashmap_num_entries(&trayIconCache) > 0 ) { + if (0!=hashmap_iterate_pairs(&trayIconCache, releaseNSObject, NULL)) { + ABORT("failed to release hashmap entries!"); + } + } + + //Free radio groups hashmap + hashmap_destroy(&trayIconCache); +} \ No newline at end of file diff --git a/v2/internal/ffenestri/traymenu_darwin.h b/v2/internal/ffenestri/traymenu_darwin.h new file mode 100644 index 000000000..763b4d63d --- /dev/null +++ b/v2/internal/ffenestri/traymenu_darwin.h @@ -0,0 +1,51 @@ +// +// Created by Lea Anthony on 12/1/21. +// + +#ifndef TRAYMENU_DARWIN_H +#define TRAYMENU_DARWIN_H + +#include "common.h" +#include "menu_darwin.h" + +typedef struct { + + const char *label; + const char *icon; + const char *ID; + const char *tooltip; + + bool templateImage; + const char *fontName; + int fontSize; + const char *RGBA; + + bool disabled; + + Menu* menu; + + id statusbaritem; + unsigned int trayIconPosition; + + JsonNode* processedJSON; + + JsonNode* styledLabel; + + id delegate; + +} TrayMenu; + +TrayMenu* NewTrayMenu(const char *trayJSON); +void DumpTrayMenu(TrayMenu* trayMenu); +void ShowTrayMenu(TrayMenu* trayMenu); +void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu); +void UpdateTrayIcon(TrayMenu *trayMenu); +void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel); + +void LoadTrayIcons(); +void UnloadTrayIcons(); + +void DeleteTrayMenu(TrayMenu* trayMenu); +void DeleteTrayMenuKeepStatusBarItem(TrayMenu* trayMenu); + +#endif //TRAYMENU_DARWIN_H diff --git a/v2/internal/ffenestri/traymenustore_darwin.c b/v2/internal/ffenestri/traymenustore_darwin.c new file mode 100644 index 000000000..362c07bf7 --- /dev/null +++ b/v2/internal/ffenestri/traymenustore_darwin.c @@ -0,0 +1,173 @@ +// +// Created by Lea Anthony on 12/1/21. +// + +#include "common.h" +#include "traymenustore_darwin.h" +#include "traymenu_darwin.h" +#include + +TrayMenuStore* NewTrayMenuStore() { + + TrayMenuStore* result = malloc(sizeof(TrayMenuStore)); + + // Allocate Tray Menu Store + if( 0 != hashmap_create((const unsigned)4, &result->trayMenuMap)) { + ABORT("[NewTrayMenuStore] Not enough memory to allocate trayMenuMap!"); + } + + if (pthread_mutex_init(&result->lock, NULL) != 0) { + printf("\n mutex init has failed\n"); + exit(1); + } + + return result; +} + +int dumpTrayMenu(void *const context, struct hashmap_element_s *const e) { + DumpTrayMenu(e->data); + return 0; +} + +void DumpTrayMenuStore(TrayMenuStore* store) { + pthread_mutex_lock(&store->lock); + hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL); + pthread_mutex_unlock(&store->lock); +} + +void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) { + + TrayMenu* newMenu = NewTrayMenu(menuJSON); + + pthread_mutex_lock(&store->lock); + //TODO: check if there is already an entry for this menu + hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu); + pthread_mutex_unlock(&store->lock); +} + +int showTrayMenu(void *const context, struct hashmap_element_s *const e) { + ShowTrayMenu(e->data); + // 0 to retain element, -1 to delete. + return 0; +} + +void ShowTrayMenusInStore(TrayMenuStore* store) { + pthread_mutex_lock(&store->lock); + if( hashmap_num_entries(&store->trayMenuMap) > 0 ) { + hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL); + } + pthread_mutex_unlock(&store->lock); +} + +int freeTrayMenu(void *const context, struct hashmap_element_s *const e) { + DeleteTrayMenu(e->data); + return -1; +} + +void DeleteTrayMenuStore(TrayMenuStore *store) { + + // Delete context menus + if (hashmap_num_entries(&store->trayMenuMap) > 0) { + if (0 != hashmap_iterate_pairs(&store->trayMenuMap, freeTrayMenu, NULL)) { + ABORT("[DeleteContextMenuStore] Failed to release contextMenuStore entries!"); + } + } + + // Destroy tray menu map + hashmap_destroy(&store->trayMenuMap); + + pthread_mutex_destroy(&store->lock); +} + +TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) { + // Get the current menu + pthread_mutex_lock(&store->lock); + TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID)); + pthread_mutex_unlock(&store->lock); + return result; +} + +TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) { + // Get the current menu + pthread_mutex_lock(&store->lock); + TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID)); + pthread_mutex_unlock(&store->lock); + + if (result == NULL ) { + ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID); + } + return result; +} + +void DeleteTrayMenuInStore(TrayMenuStore* store, const char* ID) { + + TrayMenu *menu = MustGetTrayMenuFromStore(store, ID); + pthread_mutex_lock(&store->lock); + hashmap_remove(&store->trayMenuMap, ID, strlen(ID)); + pthread_mutex_unlock(&store->lock); + DeleteTrayMenu(menu); +} + +void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) { + // Parse the JSON + JsonNode *parsedUpdate = mustParseJSON(JSON); + + // Get the data out + const char* ID = mustJSONString(parsedUpdate, "ID"); + const char* Label = mustJSONString(parsedUpdate, "Label"); + + // Check we have this menu + TrayMenu *menu = MustGetTrayMenuFromStore(store, ID); + + const char *fontName = getJSONString(parsedUpdate, "FontName"); + const char *RGBA = getJSONString(parsedUpdate, "RGBA"); + int fontSize = 0; + getJSONInt(parsedUpdate, "FontSize", &fontSize); + const char *tooltip = getJSONString(parsedUpdate, "Tooltip"); + bool disabled = false; + getJSONBool(parsedUpdate, "Disabled", &disabled); + + JsonNode *styledLabel = getJSONObject(parsedUpdate, "StyledLabel"); + + UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA, tooltip, disabled, styledLabel); + + json_delete(parsedUpdate); +} + +void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) { + TrayMenu* newMenu = NewTrayMenu(menuJSON); +// DumpTrayMenu(newMenu); + + // Get the current menu + TrayMenu *currentMenu = GetTrayMenuFromStore(store, newMenu->ID); + + // If we don't have a menu, we create one + if ( currentMenu == NULL ) { + // Store the new menu + pthread_mutex_lock(&store->lock); + hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu); + pthread_mutex_unlock(&store->lock); + + // Show it + ShowTrayMenu(newMenu); + return; + } +// DumpTrayMenu(currentMenu); + + // Save the status bar reference + newMenu->statusbaritem = currentMenu->statusbaritem; + + pthread_mutex_lock(&store->lock); + hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID)); + pthread_mutex_unlock(&store->lock); + + // Delete the current menu + DeleteTrayMenuKeepStatusBarItem(currentMenu); + + pthread_mutex_lock(&store->lock); + hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu); + pthread_mutex_unlock(&store->lock); + + // Show the updated menu + ShowTrayMenu(newMenu); +} diff --git a/v2/internal/ffenestri/traymenustore_darwin.h b/v2/internal/ffenestri/traymenustore_darwin.h new file mode 100644 index 000000000..a09a9e004 --- /dev/null +++ b/v2/internal/ffenestri/traymenustore_darwin.h @@ -0,0 +1,36 @@ +// +// Created by Lea Anthony on 7/1/21. +// + +#ifndef TRAYMENUSTORE_DARWIN_H +#define TRAYMENUSTORE_DARWIN_H + +#include "traymenu_darwin.h" + +#include + +typedef struct { + + int dummy; + + // This is our tray menu map + // It maps tray IDs to TrayMenu* + struct hashmap_s trayMenuMap; + + pthread_mutex_t lock; + +} TrayMenuStore; + +TrayMenuStore* NewTrayMenuStore(); + +void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON); +void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON); +void ShowTrayMenusInStore(TrayMenuStore* store); +void DeleteTrayMenuStore(TrayMenuStore* store); + +TrayMenu* GetTrayMenuByID(TrayMenuStore* store, const char* menuID); + +void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON); +void DeleteTrayMenuInStore(TrayMenuStore* store, const char* id); + +#endif //TRAYMENUSTORE_DARWIN_H diff --git a/v2/internal/ffenestri/vec.c b/v2/internal/ffenestri/vec.c new file mode 100644 index 000000000..6ab8bfa96 --- /dev/null +++ b/v2/internal/ffenestri/vec.c @@ -0,0 +1,115 @@ +// +build !windows + +/** + * Copyright (c) 2014 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#include "vec.h" + + +int vec_expand_(char **data, int *length, int *capacity, int memsz) { + if (*length + 1 > *capacity) { + void *ptr; + int n = (*capacity == 0) ? 1 : *capacity << 1; + ptr = realloc(*data, n * memsz); + if (ptr == NULL) return -1; + *data = ptr; + *capacity = n; + } + return 0; +} + + +int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n) { + (void) length; + if (n > *capacity) { + void *ptr = realloc(*data, n * memsz); + if (ptr == NULL) return -1; + *data = ptr; + *capacity = n; + } + return 0; +} + + +int vec_reserve_po2_( + char **data, int *length, int *capacity, int memsz, int n +) { + int n2 = 1; + if (n == 0) return 0; + while (n2 < n) n2 <<= 1; + return vec_reserve_(data, length, capacity, memsz, n2); +} + + +int vec_compact_(char **data, int *length, int *capacity, int memsz) { + if (*length == 0) { + free(*data); + *data = NULL; + *capacity = 0; + return 0; + } else { + void *ptr; + int n = *length; + ptr = realloc(*data, n * memsz); + if (ptr == NULL) return -1; + *capacity = n; + *data = ptr; + } + return 0; +} + + +int vec_insert_(char **data, int *length, int *capacity, int memsz, + int idx +) { + int err = vec_expand_(data, length, capacity, memsz); + if (err) return err; + memmove(*data + (idx + 1) * memsz, + *data + idx * memsz, + (*length - idx) * memsz); + return 0; +} + + +void vec_splice_(char **data, int *length, int *capacity, int memsz, + int start, int count +) { + (void) capacity; + memmove(*data + start * memsz, + *data + (start + count) * memsz, + (*length - start - count) * memsz); +} + + +void vec_swapsplice_(char **data, int *length, int *capacity, int memsz, + int start, int count +) { + (void) capacity; + memmove(*data + start * memsz, + *data + (*length - count) * memsz, + count * memsz); +} + + +void vec_swap_(char **data, int *length, int *capacity, int memsz, + int idx1, int idx2 +) { + unsigned char *a, *b, tmp; + int count; + (void) length; + (void) capacity; + if (idx1 == idx2) return; + a = (unsigned char*) *data + idx1 * memsz; + b = (unsigned char*) *data + idx2 * memsz; + count = memsz; + while (count--) { + tmp = *a; + *a = *b; + *b = tmp; + a++, b++; + } +} diff --git a/v2/internal/ffenestri/vec.h b/v2/internal/ffenestri/vec.h new file mode 100644 index 000000000..19362c987 --- /dev/null +++ b/v2/internal/ffenestri/vec.h @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2014 rxi + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the MIT license. See LICENSE for details. + */ + +#ifndef VEC_H +#define VEC_H + +#include +#include + +#define VEC_VERSION "0.2.1" + + +#define vec_unpack_(v)\ + (char**)&(v)->data, &(v)->length, &(v)->capacity, sizeof(*(v)->data) + + +#define vec_t(T)\ + struct { T *data; int length, capacity; } + + +#define vec_init(v)\ + memset((v), 0, sizeof(*(v))) + + +#define vec_deinit(v)\ + ( free((v)->data),\ + vec_init(v) ) + + +#define vec_push(v, val)\ + ( vec_expand_(vec_unpack_(v)) ? -1 :\ + ((v)->data[(v)->length++] = (val), 0), 0 ) + + +#define vec_pop(v)\ + (v)->data[--(v)->length] + + +#define vec_splice(v, start, count)\ + ( vec_splice_(vec_unpack_(v), start, count),\ + (v)->length -= (count) ) + + +#define vec_swapsplice(v, start, count)\ + ( vec_swapsplice_(vec_unpack_(v), start, count),\ + (v)->length -= (count) ) + + +#define vec_insert(v, idx, val)\ + ( vec_insert_(vec_unpack_(v), idx) ? -1 :\ + ((v)->data[idx] = (val), 0), (v)->length++, 0 ) + + +#define vec_sort(v, fn)\ + qsort((v)->data, (v)->length, sizeof(*(v)->data), fn) + + +#define vec_swap(v, idx1, idx2)\ + vec_swap_(vec_unpack_(v), idx1, idx2) + + +#define vec_truncate(v, len)\ + ((v)->length = (len) < (v)->length ? (len) : (v)->length) + + +#define vec_clear(v)\ + ((v)->length = 0) + + +#define vec_first(v)\ + (v)->data[0] + + +#define vec_last(v)\ + (v)->data[(v)->length - 1] + + +#define vec_reserve(v, n)\ + vec_reserve_(vec_unpack_(v), n) + + +#define vec_compact(v)\ + vec_compact_(vec_unpack_(v)) + + +#define vec_pusharr(v, arr, count)\ + do {\ + int i__, n__ = (count);\ + if (vec_reserve_po2_(vec_unpack_(v), (v)->length + n__) != 0) break;\ + for (i__ = 0; i__ < n__; i__++) {\ + (v)->data[(v)->length++] = (arr)[i__];\ + }\ + } while (0) + + +#define vec_extend(v, v2)\ + vec_pusharr((v), (v2)->data, (v2)->length) + + +#define vec_find(v, val, idx)\ + do {\ + for ((idx) = 0; (idx) < (v)->length; (idx)++) {\ + if ((v)->data[(idx)] == (val)) break;\ + }\ + if ((idx) == (v)->length) (idx) = -1;\ + } while (0) + + +#define vec_remove(v, val)\ + do {\ + int idx__;\ + vec_find(v, val, idx__);\ + if (idx__ != -1) vec_splice(v, idx__, 1);\ + } while (0) + + +#define vec_reverse(v)\ + do {\ + int i__ = (v)->length / 2;\ + while (i__--) {\ + vec_swap((v), i__, (v)->length - (i__ + 1));\ + }\ + } while (0) + + +#define vec_foreach(v, var, iter)\ + if ( (v)->length > 0 )\ + for ( (iter) = 0;\ + (iter) < (v)->length && (((var) = (v)->data[(iter)]), 1);\ + ++(iter)) + + +#define vec_foreach_rev(v, var, iter)\ + if ( (v)->length > 0 )\ + for ( (iter) = (v)->length - 1;\ + (iter) >= 0 && (((var) = (v)->data[(iter)]), 1);\ + --(iter)) + + +#define vec_foreach_ptr(v, var, iter)\ + if ( (v)->length > 0 )\ + for ( (iter) = 0;\ + (iter) < (v)->length && (((var) = &(v)->data[(iter)]), 1);\ + ++(iter)) + + +#define vec_foreach_ptr_rev(v, var, iter)\ + if ( (v)->length > 0 )\ + for ( (iter) = (v)->length - 1;\ + (iter) >= 0 && (((var) = &(v)->data[(iter)]), 1);\ + --(iter)) + + + +int vec_expand_(char **data, int *length, int *capacity, int memsz); +int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n); +int vec_reserve_po2_(char **data, int *length, int *capacity, int memsz, + int n); +int vec_compact_(char **data, int *length, int *capacity, int memsz); +int vec_insert_(char **data, int *length, int *capacity, int memsz, + int idx); +void vec_splice_(char **data, int *length, int *capacity, int memsz, + int start, int count); +void vec_swapsplice_(char **data, int *length, int *capacity, int memsz, + int start, int count); +void vec_swap_(char **data, int *length, int *capacity, int memsz, + int idx1, int idx2); + + +typedef vec_t(void*) vec_void_t; +typedef vec_t(char*) vec_str_t; +typedef vec_t(int) vec_int_t; +typedef vec_t(char) vec_char_t; +typedef vec_t(float) vec_float_t; +typedef vec_t(double) vec_double_t; + +#endif \ No newline at end of file diff --git a/v2/internal/ffenestri/windows/EventToken.h b/v2/internal/ffenestri/windows/EventToken.h new file mode 100644 index 000000000..885405b6b --- /dev/null +++ b/v2/internal/ffenestri/windows/EventToken.h @@ -0,0 +1,68 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 8.01.0622 */ +/* @@MIDL_FILE_HEADING( ) */ + + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 500 +#endif + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCSAL_H_VERSION__ +#define __REQUIRED_RPCSAL_H_VERSION__ 100 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif /* __RPCNDR_H_VERSION__ */ + + +#ifndef __eventtoken_h__ +#define __eventtoken_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +/* Forward Declarations */ + +#ifdef __cplusplus +extern "C"{ +#endif + + +/* interface __MIDL_itf_eventtoken_0000_0000 */ +/* [local] */ + +// Microsoft Windows +// Copyright (c) Microsoft Corporation. All rights reserved. +#pragma once +typedef struct EventRegistrationToken + { + __int64 value; + } EventRegistrationToken; + + + +extern RPC_IF_HANDLE __MIDL_itf_eventtoken_0000_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_eventtoken_0000_0000_v0_0_s_ifspec; + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/v2/internal/ffenestri/windows/WebView2.h b/v2/internal/ffenestri/windows/WebView2.h new file mode 100644 index 000000000..44cd67035 --- /dev/null +++ b/v2/internal/ffenestri/windows/WebView2.h @@ -0,0 +1,12693 @@ + + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + + /* File created by MIDL compiler version 8.xx.xxxx */ +/* at a redacted point in time + */ +/* Compiler settings for ../../edge_embedded_browser/client/win/current/webview2.idl: + Oicf, W1, Zp8, env=Win64 (32b run), target_arch=AMD64 8.xx.xxxx + protocol : dce , ms_ext, c_ext, robust + error checks: allocation ref bounds_check enum stub_data + VC __declspec() decoration level: + __declspec(uuid()), __declspec(selectany), __declspec(novtable) + DECLSPEC_UUID(), MIDL_INTERFACE() +*/ +/* @@MIDL_FILE_HEADING( ) */ + +#pragma warning( disable: 4049 ) /* more than 64k source lines */ + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 475 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif /* __RPCNDR_H_VERSION__ */ + + +#ifndef __webview2_h__ +#define __webview2_h__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +/* Forward Declarations */ + +#ifndef __ICoreWebView2AcceleratorKeyPressedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2AcceleratorKeyPressedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2AcceleratorKeyPressedEventArgs ICoreWebView2AcceleratorKeyPressedEventArgs; + +#endif /* __ICoreWebView2AcceleratorKeyPressedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2AcceleratorKeyPressedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2AcceleratorKeyPressedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2AcceleratorKeyPressedEventHandler ICoreWebView2AcceleratorKeyPressedEventHandler; + +#endif /* __ICoreWebView2AcceleratorKeyPressedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler; + +#endif /* __ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2CallDevToolsProtocolMethodCompletedHandler ICoreWebView2CallDevToolsProtocolMethodCompletedHandler; + +#endif /* __ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CapturePreviewCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2CapturePreviewCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2CapturePreviewCompletedHandler ICoreWebView2CapturePreviewCompletedHandler; + +#endif /* __ICoreWebView2CapturePreviewCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2_FWD_DEFINED__ +#define __ICoreWebView2_FWD_DEFINED__ +typedef interface ICoreWebView2 ICoreWebView2; + +#endif /* __ICoreWebView2_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2_2_FWD_DEFINED__ +#define __ICoreWebView2_2_FWD_DEFINED__ +typedef interface ICoreWebView2_2 ICoreWebView2_2; + +#endif /* __ICoreWebView2_2_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2_3_FWD_DEFINED__ +#define __ICoreWebView2_3_FWD_DEFINED__ +typedef interface ICoreWebView2_3 ICoreWebView2_3; + +#endif /* __ICoreWebView2_3_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CompositionController_FWD_DEFINED__ +#define __ICoreWebView2CompositionController_FWD_DEFINED__ +typedef interface ICoreWebView2CompositionController ICoreWebView2CompositionController; + +#endif /* __ICoreWebView2CompositionController_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CompositionController2_FWD_DEFINED__ +#define __ICoreWebView2CompositionController2_FWD_DEFINED__ +typedef interface ICoreWebView2CompositionController2 ICoreWebView2CompositionController2; + +#endif /* __ICoreWebView2CompositionController2_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Controller_FWD_DEFINED__ +#define __ICoreWebView2Controller_FWD_DEFINED__ +typedef interface ICoreWebView2Controller ICoreWebView2Controller; + +#endif /* __ICoreWebView2Controller_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Controller2_FWD_DEFINED__ +#define __ICoreWebView2Controller2_FWD_DEFINED__ +typedef interface ICoreWebView2Controller2 ICoreWebView2Controller2; + +#endif /* __ICoreWebView2Controller2_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Controller3_FWD_DEFINED__ +#define __ICoreWebView2Controller3_FWD_DEFINED__ +typedef interface ICoreWebView2Controller3 ICoreWebView2Controller3; + +#endif /* __ICoreWebView2Controller3_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ContentLoadingEventArgs_FWD_DEFINED__ +#define __ICoreWebView2ContentLoadingEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2ContentLoadingEventArgs ICoreWebView2ContentLoadingEventArgs; + +#endif /* __ICoreWebView2ContentLoadingEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ContentLoadingEventHandler_FWD_DEFINED__ +#define __ICoreWebView2ContentLoadingEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2ContentLoadingEventHandler ICoreWebView2ContentLoadingEventHandler; + +#endif /* __ICoreWebView2ContentLoadingEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Cookie_FWD_DEFINED__ +#define __ICoreWebView2Cookie_FWD_DEFINED__ +typedef interface ICoreWebView2Cookie ICoreWebView2Cookie; + +#endif /* __ICoreWebView2Cookie_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CookieList_FWD_DEFINED__ +#define __ICoreWebView2CookieList_FWD_DEFINED__ +typedef interface ICoreWebView2CookieList ICoreWebView2CookieList; + +#endif /* __ICoreWebView2CookieList_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CookieManager_FWD_DEFINED__ +#define __ICoreWebView2CookieManager_FWD_DEFINED__ +typedef interface ICoreWebView2CookieManager ICoreWebView2CookieManager; + +#endif /* __ICoreWebView2CookieManager_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler; + +#endif /* __ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2CreateCoreWebView2ControllerCompletedHandler ICoreWebView2CreateCoreWebView2ControllerCompletedHandler; + +#endif /* __ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler; + +#endif /* __ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ContainsFullScreenElementChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2ContainsFullScreenElementChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2ContainsFullScreenElementChangedEventHandler ICoreWebView2ContainsFullScreenElementChangedEventHandler; + +#endif /* __ICoreWebView2ContainsFullScreenElementChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CursorChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2CursorChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2CursorChangedEventHandler ICoreWebView2CursorChangedEventHandler; + +#endif /* __ICoreWebView2CursorChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2DocumentTitleChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2DocumentTitleChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2DocumentTitleChangedEventHandler ICoreWebView2DocumentTitleChangedEventHandler; + +#endif /* __ICoreWebView2DocumentTitleChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2DOMContentLoadedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2DOMContentLoadedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2DOMContentLoadedEventArgs ICoreWebView2DOMContentLoadedEventArgs; + +#endif /* __ICoreWebView2DOMContentLoadedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2DOMContentLoadedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2DOMContentLoadedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2DOMContentLoadedEventHandler ICoreWebView2DOMContentLoadedEventHandler; + +#endif /* __ICoreWebView2DOMContentLoadedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Deferral_FWD_DEFINED__ +#define __ICoreWebView2Deferral_FWD_DEFINED__ +typedef interface ICoreWebView2Deferral ICoreWebView2Deferral; + +#endif /* __ICoreWebView2Deferral_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2DevToolsProtocolEventReceivedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2DevToolsProtocolEventReceivedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2DevToolsProtocolEventReceivedEventArgs ICoreWebView2DevToolsProtocolEventReceivedEventArgs; + +#endif /* __ICoreWebView2DevToolsProtocolEventReceivedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2DevToolsProtocolEventReceivedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2DevToolsProtocolEventReceivedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2DevToolsProtocolEventReceivedEventHandler ICoreWebView2DevToolsProtocolEventReceivedEventHandler; + +#endif /* __ICoreWebView2DevToolsProtocolEventReceivedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2DevToolsProtocolEventReceiver_FWD_DEFINED__ +#define __ICoreWebView2DevToolsProtocolEventReceiver_FWD_DEFINED__ +typedef interface ICoreWebView2DevToolsProtocolEventReceiver ICoreWebView2DevToolsProtocolEventReceiver; + +#endif /* __ICoreWebView2DevToolsProtocolEventReceiver_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment_FWD_DEFINED__ +#define __ICoreWebView2Environment_FWD_DEFINED__ +typedef interface ICoreWebView2Environment ICoreWebView2Environment; + +#endif /* __ICoreWebView2Environment_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment2_FWD_DEFINED__ +#define __ICoreWebView2Environment2_FWD_DEFINED__ +typedef interface ICoreWebView2Environment2 ICoreWebView2Environment2; + +#endif /* __ICoreWebView2Environment2_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment3_FWD_DEFINED__ +#define __ICoreWebView2Environment3_FWD_DEFINED__ +typedef interface ICoreWebView2Environment3 ICoreWebView2Environment3; + +#endif /* __ICoreWebView2Environment3_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment4_FWD_DEFINED__ +#define __ICoreWebView2Environment4_FWD_DEFINED__ +typedef interface ICoreWebView2Environment4 ICoreWebView2Environment4; + +#endif /* __ICoreWebView2Environment4_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2EnvironmentOptions_FWD_DEFINED__ +#define __ICoreWebView2EnvironmentOptions_FWD_DEFINED__ +typedef interface ICoreWebView2EnvironmentOptions ICoreWebView2EnvironmentOptions; + +#endif /* __ICoreWebView2EnvironmentOptions_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ExecuteScriptCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2ExecuteScriptCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2ExecuteScriptCompletedHandler ICoreWebView2ExecuteScriptCompletedHandler; + +#endif /* __ICoreWebView2ExecuteScriptCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2FrameInfo_FWD_DEFINED__ +#define __ICoreWebView2FrameInfo_FWD_DEFINED__ +typedef interface ICoreWebView2FrameInfo ICoreWebView2FrameInfo; + +#endif /* __ICoreWebView2FrameInfo_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2FrameInfoCollection_FWD_DEFINED__ +#define __ICoreWebView2FrameInfoCollection_FWD_DEFINED__ +typedef interface ICoreWebView2FrameInfoCollection ICoreWebView2FrameInfoCollection; + +#endif /* __ICoreWebView2FrameInfoCollection_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2FrameInfoCollectionIterator_FWD_DEFINED__ +#define __ICoreWebView2FrameInfoCollectionIterator_FWD_DEFINED__ +typedef interface ICoreWebView2FrameInfoCollectionIterator ICoreWebView2FrameInfoCollectionIterator; + +#endif /* __ICoreWebView2FrameInfoCollectionIterator_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2FocusChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2FocusChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2FocusChangedEventHandler ICoreWebView2FocusChangedEventHandler; + +#endif /* __ICoreWebView2FocusChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2GetCookiesCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2GetCookiesCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2GetCookiesCompletedHandler ICoreWebView2GetCookiesCompletedHandler; + +#endif /* __ICoreWebView2GetCookiesCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2HistoryChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2HistoryChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2HistoryChangedEventHandler ICoreWebView2HistoryChangedEventHandler; + +#endif /* __ICoreWebView2HistoryChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2HttpHeadersCollectionIterator_FWD_DEFINED__ +#define __ICoreWebView2HttpHeadersCollectionIterator_FWD_DEFINED__ +typedef interface ICoreWebView2HttpHeadersCollectionIterator ICoreWebView2HttpHeadersCollectionIterator; + +#endif /* __ICoreWebView2HttpHeadersCollectionIterator_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2HttpRequestHeaders_FWD_DEFINED__ +#define __ICoreWebView2HttpRequestHeaders_FWD_DEFINED__ +typedef interface ICoreWebView2HttpRequestHeaders ICoreWebView2HttpRequestHeaders; + +#endif /* __ICoreWebView2HttpRequestHeaders_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2HttpResponseHeaders_FWD_DEFINED__ +#define __ICoreWebView2HttpResponseHeaders_FWD_DEFINED__ +typedef interface ICoreWebView2HttpResponseHeaders ICoreWebView2HttpResponseHeaders; + +#endif /* __ICoreWebView2HttpResponseHeaders_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Interop_FWD_DEFINED__ +#define __ICoreWebView2Interop_FWD_DEFINED__ +typedef interface ICoreWebView2Interop ICoreWebView2Interop; + +#endif /* __ICoreWebView2Interop_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2MoveFocusRequestedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2MoveFocusRequestedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2MoveFocusRequestedEventArgs ICoreWebView2MoveFocusRequestedEventArgs; + +#endif /* __ICoreWebView2MoveFocusRequestedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2MoveFocusRequestedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2MoveFocusRequestedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2MoveFocusRequestedEventHandler ICoreWebView2MoveFocusRequestedEventHandler; + +#endif /* __ICoreWebView2MoveFocusRequestedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationCompletedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2NavigationCompletedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2NavigationCompletedEventArgs ICoreWebView2NavigationCompletedEventArgs; + +#endif /* __ICoreWebView2NavigationCompletedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationCompletedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2NavigationCompletedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2NavigationCompletedEventHandler ICoreWebView2NavigationCompletedEventHandler; + +#endif /* __ICoreWebView2NavigationCompletedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationStartingEventArgs_FWD_DEFINED__ +#define __ICoreWebView2NavigationStartingEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2NavigationStartingEventArgs ICoreWebView2NavigationStartingEventArgs; + +#endif /* __ICoreWebView2NavigationStartingEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationStartingEventHandler_FWD_DEFINED__ +#define __ICoreWebView2NavigationStartingEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2NavigationStartingEventHandler ICoreWebView2NavigationStartingEventHandler; + +#endif /* __ICoreWebView2NavigationStartingEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2NewBrowserVersionAvailableEventHandler_FWD_DEFINED__ +#define __ICoreWebView2NewBrowserVersionAvailableEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2NewBrowserVersionAvailableEventHandler ICoreWebView2NewBrowserVersionAvailableEventHandler; + +#endif /* __ICoreWebView2NewBrowserVersionAvailableEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2NewWindowRequestedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2NewWindowRequestedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2NewWindowRequestedEventArgs ICoreWebView2NewWindowRequestedEventArgs; + +#endif /* __ICoreWebView2NewWindowRequestedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2NewWindowRequestedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2NewWindowRequestedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2NewWindowRequestedEventHandler ICoreWebView2NewWindowRequestedEventHandler; + +#endif /* __ICoreWebView2NewWindowRequestedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2PermissionRequestedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2PermissionRequestedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2PermissionRequestedEventArgs ICoreWebView2PermissionRequestedEventArgs; + +#endif /* __ICoreWebView2PermissionRequestedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2PermissionRequestedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2PermissionRequestedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2PermissionRequestedEventHandler ICoreWebView2PermissionRequestedEventHandler; + +#endif /* __ICoreWebView2PermissionRequestedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2PointerInfo_FWD_DEFINED__ +#define __ICoreWebView2PointerInfo_FWD_DEFINED__ +typedef interface ICoreWebView2PointerInfo ICoreWebView2PointerInfo; + +#endif /* __ICoreWebView2PointerInfo_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ProcessFailedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2ProcessFailedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2ProcessFailedEventArgs ICoreWebView2ProcessFailedEventArgs; + +#endif /* __ICoreWebView2ProcessFailedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ProcessFailedEventArgs2_FWD_DEFINED__ +#define __ICoreWebView2ProcessFailedEventArgs2_FWD_DEFINED__ +typedef interface ICoreWebView2ProcessFailedEventArgs2 ICoreWebView2ProcessFailedEventArgs2; + +#endif /* __ICoreWebView2ProcessFailedEventArgs2_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ProcessFailedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2ProcessFailedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2ProcessFailedEventHandler ICoreWebView2ProcessFailedEventHandler; + +#endif /* __ICoreWebView2ProcessFailedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2RasterizationScaleChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2RasterizationScaleChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2RasterizationScaleChangedEventHandler ICoreWebView2RasterizationScaleChangedEventHandler; + +#endif /* __ICoreWebView2RasterizationScaleChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ScriptDialogOpeningEventArgs_FWD_DEFINED__ +#define __ICoreWebView2ScriptDialogOpeningEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2ScriptDialogOpeningEventArgs ICoreWebView2ScriptDialogOpeningEventArgs; + +#endif /* __ICoreWebView2ScriptDialogOpeningEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ScriptDialogOpeningEventHandler_FWD_DEFINED__ +#define __ICoreWebView2ScriptDialogOpeningEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2ScriptDialogOpeningEventHandler ICoreWebView2ScriptDialogOpeningEventHandler; + +#endif /* __ICoreWebView2ScriptDialogOpeningEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Settings_FWD_DEFINED__ +#define __ICoreWebView2Settings_FWD_DEFINED__ +typedef interface ICoreWebView2Settings ICoreWebView2Settings; + +#endif /* __ICoreWebView2Settings_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Settings2_FWD_DEFINED__ +#define __ICoreWebView2Settings2_FWD_DEFINED__ +typedef interface ICoreWebView2Settings2 ICoreWebView2Settings2; + +#endif /* __ICoreWebView2Settings2_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2Settings3_FWD_DEFINED__ +#define __ICoreWebView2Settings3_FWD_DEFINED__ +typedef interface ICoreWebView2Settings3 ICoreWebView2Settings3; + +#endif /* __ICoreWebView2Settings3_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2SourceChangedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2SourceChangedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2SourceChangedEventArgs ICoreWebView2SourceChangedEventArgs; + +#endif /* __ICoreWebView2SourceChangedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2SourceChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2SourceChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2SourceChangedEventHandler ICoreWebView2SourceChangedEventHandler; + +#endif /* __ICoreWebView2SourceChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2TrySuspendCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2TrySuspendCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2TrySuspendCompletedHandler ICoreWebView2TrySuspendCompletedHandler; + +#endif /* __ICoreWebView2TrySuspendCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebMessageReceivedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2WebMessageReceivedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2WebMessageReceivedEventArgs ICoreWebView2WebMessageReceivedEventArgs; + +#endif /* __ICoreWebView2WebMessageReceivedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebMessageReceivedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2WebMessageReceivedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2WebMessageReceivedEventHandler ICoreWebView2WebMessageReceivedEventHandler; + +#endif /* __ICoreWebView2WebMessageReceivedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceRequest_FWD_DEFINED__ +#define __ICoreWebView2WebResourceRequest_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceRequest ICoreWebView2WebResourceRequest; + +#endif /* __ICoreWebView2WebResourceRequest_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceRequestedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2WebResourceRequestedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceRequestedEventArgs ICoreWebView2WebResourceRequestedEventArgs; + +#endif /* __ICoreWebView2WebResourceRequestedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceRequestedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2WebResourceRequestedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceRequestedEventHandler ICoreWebView2WebResourceRequestedEventHandler; + +#endif /* __ICoreWebView2WebResourceRequestedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponse_FWD_DEFINED__ +#define __ICoreWebView2WebResourceResponse_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceResponse ICoreWebView2WebResourceResponse; + +#endif /* __ICoreWebView2WebResourceResponse_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseReceivedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2WebResourceResponseReceivedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceResponseReceivedEventHandler ICoreWebView2WebResourceResponseReceivedEventHandler; + +#endif /* __ICoreWebView2WebResourceResponseReceivedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseReceivedEventArgs_FWD_DEFINED__ +#define __ICoreWebView2WebResourceResponseReceivedEventArgs_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceResponseReceivedEventArgs ICoreWebView2WebResourceResponseReceivedEventArgs; + +#endif /* __ICoreWebView2WebResourceResponseReceivedEventArgs_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseView_FWD_DEFINED__ +#define __ICoreWebView2WebResourceResponseView_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceResponseView ICoreWebView2WebResourceResponseView; + +#endif /* __ICoreWebView2WebResourceResponseView_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_FWD_DEFINED__ +#define __ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_FWD_DEFINED__ +typedef interface ICoreWebView2WebResourceResponseViewGetContentCompletedHandler ICoreWebView2WebResourceResponseViewGetContentCompletedHandler; + +#endif /* __ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WindowCloseRequestedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2WindowCloseRequestedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2WindowCloseRequestedEventHandler ICoreWebView2WindowCloseRequestedEventHandler; + +#endif /* __ICoreWebView2WindowCloseRequestedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2WindowFeatures_FWD_DEFINED__ +#define __ICoreWebView2WindowFeatures_FWD_DEFINED__ +typedef interface ICoreWebView2WindowFeatures ICoreWebView2WindowFeatures; + +#endif /* __ICoreWebView2WindowFeatures_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2ZoomFactorChangedEventHandler_FWD_DEFINED__ +#define __ICoreWebView2ZoomFactorChangedEventHandler_FWD_DEFINED__ +typedef interface ICoreWebView2ZoomFactorChangedEventHandler ICoreWebView2ZoomFactorChangedEventHandler; + +#endif /* __ICoreWebView2ZoomFactorChangedEventHandler_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2CompositionControllerInterop_FWD_DEFINED__ +#define __ICoreWebView2CompositionControllerInterop_FWD_DEFINED__ +typedef interface ICoreWebView2CompositionControllerInterop ICoreWebView2CompositionControllerInterop; + +#endif /* __ICoreWebView2CompositionControllerInterop_FWD_DEFINED__ */ + + +#ifndef __ICoreWebView2EnvironmentInterop_FWD_DEFINED__ +#define __ICoreWebView2EnvironmentInterop_FWD_DEFINED__ +typedef interface ICoreWebView2EnvironmentInterop ICoreWebView2EnvironmentInterop; + +#endif /* __ICoreWebView2EnvironmentInterop_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "objidl.h" +#include "oaidl.h" +#include "EventToken.h" + +#ifdef __cplusplus +extern "C"{ +#endif + + + +#ifndef __WebView2_LIBRARY_DEFINED__ +#define __WebView2_LIBRARY_DEFINED__ + +/* library WebView2 */ +/* [version][uuid] */ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT + { + COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT_PNG = 0, + COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT_JPEG = ( COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT_PNG + 1 ) + } COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_COOKIE_SAME_SITE_KIND + { + COREWEBVIEW2_COOKIE_SAME_SITE_KIND_NONE = 0, + COREWEBVIEW2_COOKIE_SAME_SITE_KIND_LAX = ( COREWEBVIEW2_COOKIE_SAME_SITE_KIND_NONE + 1 ) , + COREWEBVIEW2_COOKIE_SAME_SITE_KIND_STRICT = ( COREWEBVIEW2_COOKIE_SAME_SITE_KIND_LAX + 1 ) + } COREWEBVIEW2_COOKIE_SAME_SITE_KIND; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND + { + COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY = 0, + COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW = ( COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY + 1 ) , + COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS = ( COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW + 1 ) + } COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_SCRIPT_DIALOG_KIND + { + COREWEBVIEW2_SCRIPT_DIALOG_KIND_ALERT = 0, + COREWEBVIEW2_SCRIPT_DIALOG_KIND_CONFIRM = ( COREWEBVIEW2_SCRIPT_DIALOG_KIND_ALERT + 1 ) , + COREWEBVIEW2_SCRIPT_DIALOG_KIND_PROMPT = ( COREWEBVIEW2_SCRIPT_DIALOG_KIND_CONFIRM + 1 ) , + COREWEBVIEW2_SCRIPT_DIALOG_KIND_BEFOREUNLOAD = ( COREWEBVIEW2_SCRIPT_DIALOG_KIND_PROMPT + 1 ) + } COREWEBVIEW2_SCRIPT_DIALOG_KIND; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_PROCESS_FAILED_KIND + { + COREWEBVIEW2_PROCESS_FAILED_KIND_BROWSER_PROCESS_EXITED = 0, + COREWEBVIEW2_PROCESS_FAILED_KIND_RENDER_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_BROWSER_PROCESS_EXITED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_RENDER_PROCESS_UNRESPONSIVE = ( COREWEBVIEW2_PROCESS_FAILED_KIND_RENDER_PROCESS_EXITED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_FRAME_RENDER_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_RENDER_PROCESS_UNRESPONSIVE + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_UTILITY_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_FRAME_RENDER_PROCESS_EXITED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_SANDBOX_HELPER_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_UTILITY_PROCESS_EXITED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_GPU_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_SANDBOX_HELPER_PROCESS_EXITED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_PPAPI_PLUGIN_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_GPU_PROCESS_EXITED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_PPAPI_BROKER_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_PPAPI_PLUGIN_PROCESS_EXITED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_KIND_UNKNOWN_PROCESS_EXITED = ( COREWEBVIEW2_PROCESS_FAILED_KIND_PPAPI_BROKER_PROCESS_EXITED + 1 ) + } COREWEBVIEW2_PROCESS_FAILED_KIND; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_PROCESS_FAILED_REASON + { + COREWEBVIEW2_PROCESS_FAILED_REASON_UNEXPECTED = 0, + COREWEBVIEW2_PROCESS_FAILED_REASON_UNRESPONSIVE = ( COREWEBVIEW2_PROCESS_FAILED_REASON_UNEXPECTED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_REASON_TERMINATED = ( COREWEBVIEW2_PROCESS_FAILED_REASON_UNRESPONSIVE + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_REASON_CRASHED = ( COREWEBVIEW2_PROCESS_FAILED_REASON_TERMINATED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_REASON_LAUNCH_FAILED = ( COREWEBVIEW2_PROCESS_FAILED_REASON_CRASHED + 1 ) , + COREWEBVIEW2_PROCESS_FAILED_REASON_OUT_OF_MEMORY = ( COREWEBVIEW2_PROCESS_FAILED_REASON_LAUNCH_FAILED + 1 ) + } COREWEBVIEW2_PROCESS_FAILED_REASON; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_PERMISSION_KIND + { + COREWEBVIEW2_PERMISSION_KIND_UNKNOWN_PERMISSION = 0, + COREWEBVIEW2_PERMISSION_KIND_MICROPHONE = ( COREWEBVIEW2_PERMISSION_KIND_UNKNOWN_PERMISSION + 1 ) , + COREWEBVIEW2_PERMISSION_KIND_CAMERA = ( COREWEBVIEW2_PERMISSION_KIND_MICROPHONE + 1 ) , + COREWEBVIEW2_PERMISSION_KIND_GEOLOCATION = ( COREWEBVIEW2_PERMISSION_KIND_CAMERA + 1 ) , + COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS = ( COREWEBVIEW2_PERMISSION_KIND_GEOLOCATION + 1 ) , + COREWEBVIEW2_PERMISSION_KIND_OTHER_SENSORS = ( COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS + 1 ) , + COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ = ( COREWEBVIEW2_PERMISSION_KIND_OTHER_SENSORS + 1 ) + } COREWEBVIEW2_PERMISSION_KIND; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_PERMISSION_STATE + { + COREWEBVIEW2_PERMISSION_STATE_DEFAULT = 0, + COREWEBVIEW2_PERMISSION_STATE_ALLOW = ( COREWEBVIEW2_PERMISSION_STATE_DEFAULT + 1 ) , + COREWEBVIEW2_PERMISSION_STATE_DENY = ( COREWEBVIEW2_PERMISSION_STATE_ALLOW + 1 ) + } COREWEBVIEW2_PERMISSION_STATE; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_WEB_ERROR_STATUS + { + COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN = 0, + COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT = ( COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_EXPIRED = ( COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_CLIENT_CERTIFICATE_CONTAINS_ERRORS = ( COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_EXPIRED + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_REVOKED = ( COREWEBVIEW2_WEB_ERROR_STATUS_CLIENT_CERTIFICATE_CONTAINS_ERRORS + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID = ( COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_REVOKED + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_SERVER_UNREACHABLE = ( COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_TIMEOUT = ( COREWEBVIEW2_WEB_ERROR_STATUS_SERVER_UNREACHABLE + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_ERROR_HTTP_INVALID_SERVER_RESPONSE = ( COREWEBVIEW2_WEB_ERROR_STATUS_TIMEOUT + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED = ( COREWEBVIEW2_WEB_ERROR_STATUS_ERROR_HTTP_INVALID_SERVER_RESPONSE + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_RESET = ( COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED = ( COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_RESET + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_CANNOT_CONNECT = ( COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_HOST_NAME_NOT_RESOLVED = ( COREWEBVIEW2_WEB_ERROR_STATUS_CANNOT_CONNECT + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED = ( COREWEBVIEW2_WEB_ERROR_STATUS_HOST_NAME_NOT_RESOLVED + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_REDIRECT_FAILED = ( COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED + 1 ) , + COREWEBVIEW2_WEB_ERROR_STATUS_UNEXPECTED_ERROR = ( COREWEBVIEW2_WEB_ERROR_STATUS_REDIRECT_FAILED + 1 ) + } COREWEBVIEW2_WEB_ERROR_STATUS; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_WEB_RESOURCE_CONTEXT + { + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL = 0, + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_STYLESHEET = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_STYLESHEET + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MEDIA = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FONT = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MEDIA + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SCRIPT = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FONT + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_XML_HTTP_REQUEST = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SCRIPT + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FETCH = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_XML_HTTP_REQUEST + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_TEXT_TRACK = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_FETCH + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_EVENT_SOURCE = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_TEXT_TRACK + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_WEBSOCKET = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_EVENT_SOURCE + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MANIFEST = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_WEBSOCKET + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SIGNED_EXCHANGE = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_MANIFEST + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_PING = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_SIGNED_EXCHANGE + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_CSP_VIOLATION_REPORT = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_PING + 1 ) , + COREWEBVIEW2_WEB_RESOURCE_CONTEXT_OTHER = ( COREWEBVIEW2_WEB_RESOURCE_CONTEXT_CSP_VIOLATION_REPORT + 1 ) + } COREWEBVIEW2_WEB_RESOURCE_CONTEXT; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_MOVE_FOCUS_REASON + { + COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC = 0, + COREWEBVIEW2_MOVE_FOCUS_REASON_NEXT = ( COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC + 1 ) , + COREWEBVIEW2_MOVE_FOCUS_REASON_PREVIOUS = ( COREWEBVIEW2_MOVE_FOCUS_REASON_NEXT + 1 ) + } COREWEBVIEW2_MOVE_FOCUS_REASON; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_KEY_EVENT_KIND + { + COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN = 0, + COREWEBVIEW2_KEY_EVENT_KIND_KEY_UP = ( COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN + 1 ) , + COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN = ( COREWEBVIEW2_KEY_EVENT_KIND_KEY_UP + 1 ) , + COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_UP = ( COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN + 1 ) + } COREWEBVIEW2_KEY_EVENT_KIND; + +typedef struct COREWEBVIEW2_PHYSICAL_KEY_STATUS + { + UINT32 RepeatCount; + UINT32 ScanCode; + BOOL IsExtendedKey; + BOOL IsMenuKeyDown; + BOOL WasKeyDown; + BOOL IsKeyReleased; + } COREWEBVIEW2_PHYSICAL_KEY_STATUS; + +typedef struct COREWEBVIEW2_COLOR + { + BYTE A; + BYTE R; + BYTE G; + BYTE B; + } COREWEBVIEW2_COLOR; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_MOUSE_EVENT_KIND + { + COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL = 0x20e, + COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_DOUBLE_CLICK = 0x203, + COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_DOWN = 0x201, + COREWEBVIEW2_MOUSE_EVENT_KIND_LEFT_BUTTON_UP = 0x202, + COREWEBVIEW2_MOUSE_EVENT_KIND_LEAVE = 0x2a3, + COREWEBVIEW2_MOUSE_EVENT_KIND_MIDDLE_BUTTON_DOUBLE_CLICK = 0x209, + COREWEBVIEW2_MOUSE_EVENT_KIND_MIDDLE_BUTTON_DOWN = 0x207, + COREWEBVIEW2_MOUSE_EVENT_KIND_MIDDLE_BUTTON_UP = 0x208, + COREWEBVIEW2_MOUSE_EVENT_KIND_MOVE = 0x200, + COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_DOUBLE_CLICK = 0x206, + COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_DOWN = 0x204, + COREWEBVIEW2_MOUSE_EVENT_KIND_RIGHT_BUTTON_UP = 0x205, + COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL = 0x20a, + COREWEBVIEW2_MOUSE_EVENT_KIND_X_BUTTON_DOUBLE_CLICK = 0x20d, + COREWEBVIEW2_MOUSE_EVENT_KIND_X_BUTTON_DOWN = 0x20b, + COREWEBVIEW2_MOUSE_EVENT_KIND_X_BUTTON_UP = 0x20c + } COREWEBVIEW2_MOUSE_EVENT_KIND; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS + { + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_NONE = 0, + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_LEFT_BUTTON = 0x1, + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_RIGHT_BUTTON = 0x2, + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_SHIFT = 0x4, + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_CONTROL = 0x8, + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_MIDDLE_BUTTON = 0x10, + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_X_BUTTON1 = 0x20, + COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS_X_BUTTON2 = 0x40 + } COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS; + +DEFINE_ENUM_FLAG_OPERATORS(COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS); +typedef /* [v1_enum] */ +enum COREWEBVIEW2_POINTER_EVENT_KIND + { + COREWEBVIEW2_POINTER_EVENT_KIND_ACTIVATE = 0x24b, + COREWEBVIEW2_POINTER_EVENT_KIND_DOWN = 0x246, + COREWEBVIEW2_POINTER_EVENT_KIND_ENTER = 0x249, + COREWEBVIEW2_POINTER_EVENT_KIND_LEAVE = 0x24a, + COREWEBVIEW2_POINTER_EVENT_KIND_UP = 0x247, + COREWEBVIEW2_POINTER_EVENT_KIND_UPDATE = 0x245 + } COREWEBVIEW2_POINTER_EVENT_KIND; + +typedef /* [v1_enum] */ +enum COREWEBVIEW2_BOUNDS_MODE + { + COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS = 0, + COREWEBVIEW2_BOUNDS_MODE_USE_RASTERIZATION_SCALE = ( COREWEBVIEW2_BOUNDS_MODE_USE_RAW_PIXELS + 1 ) + } COREWEBVIEW2_BOUNDS_MODE; + +STDAPI CreateCoreWebView2EnvironmentWithOptions(PCWSTR browserExecutableFolder, PCWSTR userDataFolder, ICoreWebView2EnvironmentOptions* environmentOptions, ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* environmentCreatedHandler); +STDAPI CreateCoreWebView2Environment(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* environmentCreatedHandler); +STDAPI GetAvailableCoreWebView2BrowserVersionString(PCWSTR browserExecutableFolder, LPWSTR* versionInfo); +STDAPI CompareBrowserVersions(PCWSTR version1, PCWSTR version2, int* result); + +EXTERN_C const IID LIBID_WebView2; + +#ifndef __ICoreWebView2AcceleratorKeyPressedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2AcceleratorKeyPressedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2AcceleratorKeyPressedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2AcceleratorKeyPressedEventArgs = {0x9f760f8a,0xfb79,0x42be,{0x99,0x90,0x7b,0x56,0x90,0x0f,0xa9,0xc7}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9f760f8a-fb79-42be-9990-7b56900fa9c7") + ICoreWebView2AcceleratorKeyPressedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_KeyEventKind( + /* [retval][out] */ COREWEBVIEW2_KEY_EVENT_KIND *keyEventKind) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_VirtualKey( + /* [retval][out] */ UINT *virtualKey) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_KeyEventLParam( + /* [retval][out] */ INT *lParam) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PhysicalKeyStatus( + /* [retval][out] */ COREWEBVIEW2_PHYSICAL_KEY_STATUS *physicalKeyStatus) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Handled( + /* [retval][out] */ BOOL *handled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Handled( + /* [in] */ BOOL handled) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2AcceleratorKeyPressedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_KeyEventKind )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_KEY_EVENT_KIND *keyEventKind); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_VirtualKey )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This, + /* [retval][out] */ UINT *virtualKey); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_KeyEventLParam )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This, + /* [retval][out] */ INT *lParam); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PhysicalKeyStatus )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_PHYSICAL_KEY_STATUS *physicalKeyStatus); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Handled )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This, + /* [retval][out] */ BOOL *handled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Handled )( + ICoreWebView2AcceleratorKeyPressedEventArgs * This, + /* [in] */ BOOL handled); + + END_INTERFACE + } ICoreWebView2AcceleratorKeyPressedEventArgsVtbl; + + interface ICoreWebView2AcceleratorKeyPressedEventArgs + { + CONST_VTBL struct ICoreWebView2AcceleratorKeyPressedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_get_KeyEventKind(This,keyEventKind) \ + ( (This)->lpVtbl -> get_KeyEventKind(This,keyEventKind) ) + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_get_VirtualKey(This,virtualKey) \ + ( (This)->lpVtbl -> get_VirtualKey(This,virtualKey) ) + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_get_KeyEventLParam(This,lParam) \ + ( (This)->lpVtbl -> get_KeyEventLParam(This,lParam) ) + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_get_PhysicalKeyStatus(This,physicalKeyStatus) \ + ( (This)->lpVtbl -> get_PhysicalKeyStatus(This,physicalKeyStatus) ) + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_get_Handled(This,handled) \ + ( (This)->lpVtbl -> get_Handled(This,handled) ) + +#define ICoreWebView2AcceleratorKeyPressedEventArgs_put_Handled(This,handled) \ + ( (This)->lpVtbl -> put_Handled(This,handled) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2AcceleratorKeyPressedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2AcceleratorKeyPressedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2AcceleratorKeyPressedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2AcceleratorKeyPressedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2AcceleratorKeyPressedEventHandler = {0xb29c7e28,0xfa79,0x41a8,{0x8e,0x44,0x65,0x81,0x1c,0x76,0xdc,0xb2}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("b29c7e28-fa79-41a8-8e44-65811c76dcb2") + ICoreWebView2AcceleratorKeyPressedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ ICoreWebView2AcceleratorKeyPressedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2AcceleratorKeyPressedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2AcceleratorKeyPressedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2AcceleratorKeyPressedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2AcceleratorKeyPressedEventHandler * This, + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ ICoreWebView2AcceleratorKeyPressedEventArgs *args); + + END_INTERFACE + } ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl; + + interface ICoreWebView2AcceleratorKeyPressedEventHandler + { + CONST_VTBL struct ICoreWebView2AcceleratorKeyPressedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2AcceleratorKeyPressedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2AcceleratorKeyPressedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2AcceleratorKeyPressedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2AcceleratorKeyPressedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2AcceleratorKeyPressedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler = {0xb99369f3,0x9b11,0x47b5,{0xbc,0x6f,0x8e,0x78,0x95,0xfc,0xea,0x17}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("b99369f3-9b11-47b5-bc6f-8e7895fcea17") + ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ HRESULT errorCode, + /* [in] */ LPCWSTR id) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler * This, + /* [in] */ HRESULT errorCode, + /* [in] */ LPCWSTR id); + + END_INTERFACE + } ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVtbl; + + interface ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler + { + CONST_VTBL struct ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_Invoke(This,errorCode,id) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,id) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CallDevToolsProtocolMethodCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CallDevToolsProtocolMethodCompletedHandler = {0x5c4889f0,0x5ef6,0x4c5a,{0x95,0x2c,0xd8,0xf1,0xb9,0x2d,0x05,0x74}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("5c4889f0-5ef6-4c5a-952c-d8f1b92d0574") + ICoreWebView2CallDevToolsProtocolMethodCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ HRESULT errorCode, + /* [in] */ LPCWSTR returnObjectAsJson) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CallDevToolsProtocolMethodCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CallDevToolsProtocolMethodCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CallDevToolsProtocolMethodCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CallDevToolsProtocolMethodCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2CallDevToolsProtocolMethodCompletedHandler * This, + /* [in] */ HRESULT errorCode, + /* [in] */ LPCWSTR returnObjectAsJson); + + END_INTERFACE + } ICoreWebView2CallDevToolsProtocolMethodCompletedHandlerVtbl; + + interface ICoreWebView2CallDevToolsProtocolMethodCompletedHandler + { + CONST_VTBL struct ICoreWebView2CallDevToolsProtocolMethodCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_Invoke(This,errorCode,returnObjectAsJson) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,returnObjectAsJson) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CallDevToolsProtocolMethodCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CapturePreviewCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2CapturePreviewCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CapturePreviewCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CapturePreviewCompletedHandler = {0x697e05e9,0x3d8f,0x45fa,{0x96,0xf4,0x8f,0xfe,0x1e,0xde,0xda,0xf5}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("697e05e9-3d8f-45fa-96f4-8ffe1ededaf5") + ICoreWebView2CapturePreviewCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ HRESULT errorCode) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CapturePreviewCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CapturePreviewCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CapturePreviewCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CapturePreviewCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2CapturePreviewCompletedHandler * This, + /* [in] */ HRESULT errorCode); + + END_INTERFACE + } ICoreWebView2CapturePreviewCompletedHandlerVtbl; + + interface ICoreWebView2CapturePreviewCompletedHandler + { + CONST_VTBL struct ICoreWebView2CapturePreviewCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CapturePreviewCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CapturePreviewCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CapturePreviewCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CapturePreviewCompletedHandler_Invoke(This,errorCode) \ + ( (This)->lpVtbl -> Invoke(This,errorCode) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CapturePreviewCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2_INTERFACE_DEFINED__ +#define __ICoreWebView2_INTERFACE_DEFINED__ + +/* interface ICoreWebView2 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2 = {0x76eceacb,0x0462,0x4d94,{0xac,0x83,0x42,0x3a,0x67,0x93,0x77,0x5e}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("76eceacb-0462-4d94-ac83-423a6793775e") + ICoreWebView2 : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Settings( + /* [retval][out] */ ICoreWebView2Settings **settings) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Source( + /* [retval][out] */ LPWSTR *uri) = 0; + + virtual HRESULT STDMETHODCALLTYPE Navigate( + /* [in] */ LPCWSTR uri) = 0; + + virtual HRESULT STDMETHODCALLTYPE NavigateToString( + /* [in] */ LPCWSTR htmlContent) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_NavigationStarting( + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_NavigationStarting( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_ContentLoading( + /* [in] */ ICoreWebView2ContentLoadingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_ContentLoading( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_SourceChanged( + /* [in] */ ICoreWebView2SourceChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_SourceChanged( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_HistoryChanged( + /* [in] */ ICoreWebView2HistoryChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_HistoryChanged( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_NavigationCompleted( + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_NavigationCompleted( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_FrameNavigationStarting( + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_FrameNavigationStarting( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_FrameNavigationCompleted( + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_FrameNavigationCompleted( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_ScriptDialogOpening( + /* [in] */ ICoreWebView2ScriptDialogOpeningEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_ScriptDialogOpening( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_PermissionRequested( + /* [in] */ ICoreWebView2PermissionRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_PermissionRequested( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_ProcessFailed( + /* [in] */ ICoreWebView2ProcessFailedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_ProcessFailed( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddScriptToExecuteOnDocumentCreated( + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler *handler) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveScriptToExecuteOnDocumentCreated( + /* [in] */ LPCWSTR id) = 0; + + virtual HRESULT STDMETHODCALLTYPE ExecuteScript( + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2ExecuteScriptCompletedHandler *handler) = 0; + + virtual HRESULT STDMETHODCALLTYPE CapturePreview( + /* [in] */ COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT imageFormat, + /* [in] */ IStream *imageStream, + /* [in] */ ICoreWebView2CapturePreviewCompletedHandler *handler) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reload( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE PostWebMessageAsJson( + /* [in] */ LPCWSTR webMessageAsJson) = 0; + + virtual HRESULT STDMETHODCALLTYPE PostWebMessageAsString( + /* [in] */ LPCWSTR webMessageAsString) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_WebMessageReceived( + /* [in] */ ICoreWebView2WebMessageReceivedEventHandler *handler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_WebMessageReceived( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE CallDevToolsProtocolMethod( + /* [in] */ LPCWSTR methodName, + /* [in] */ LPCWSTR parametersAsJson, + /* [in] */ ICoreWebView2CallDevToolsProtocolMethodCompletedHandler *handler) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_BrowserProcessId( + /* [retval][out] */ UINT32 *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CanGoBack( + /* [retval][out] */ BOOL *canGoBack) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CanGoForward( + /* [retval][out] */ BOOL *canGoForward) = 0; + + virtual HRESULT STDMETHODCALLTYPE GoBack( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GoForward( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDevToolsProtocolEventReceiver( + /* [in] */ LPCWSTR eventName, + /* [retval][out] */ ICoreWebView2DevToolsProtocolEventReceiver **receiver) = 0; + + virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_NewWindowRequested( + /* [in] */ ICoreWebView2NewWindowRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_NewWindowRequested( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_DocumentTitleChanged( + /* [in] */ ICoreWebView2DocumentTitleChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_DocumentTitleChanged( + /* [in] */ EventRegistrationToken token) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_DocumentTitle( + /* [retval][out] */ LPWSTR *title) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddHostObjectToScript( + /* [in] */ LPCWSTR name, + /* [in] */ VARIANT *object) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveHostObjectFromScript( + /* [in] */ LPCWSTR name) = 0; + + virtual HRESULT STDMETHODCALLTYPE OpenDevToolsWindow( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_ContainsFullScreenElementChanged( + /* [in] */ ICoreWebView2ContainsFullScreenElementChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_ContainsFullScreenElementChanged( + /* [in] */ EventRegistrationToken token) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ContainsFullScreenElement( + /* [retval][out] */ BOOL *containsFullScreenElement) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_WebResourceRequested( + /* [in] */ ICoreWebView2WebResourceRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_WebResourceRequested( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddWebResourceRequestedFilter( + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveWebResourceRequestedFilter( + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_WindowCloseRequested( + /* [in] */ ICoreWebView2WindowCloseRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_WindowCloseRequested( + /* [in] */ EventRegistrationToken token) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Settings )( + ICoreWebView2 * This, + /* [retval][out] */ ICoreWebView2Settings **settings); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Source )( + ICoreWebView2 * This, + /* [retval][out] */ LPWSTR *uri); + + HRESULT ( STDMETHODCALLTYPE *Navigate )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR uri); + + HRESULT ( STDMETHODCALLTYPE *NavigateToString )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR htmlContent); + + HRESULT ( STDMETHODCALLTYPE *add_NavigationStarting )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NavigationStarting )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ContentLoading )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2ContentLoadingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ContentLoading )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_SourceChanged )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2SourceChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_SourceChanged )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_HistoryChanged )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2HistoryChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_HistoryChanged )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_NavigationCompleted )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NavigationCompleted )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_FrameNavigationStarting )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_FrameNavigationStarting )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_FrameNavigationCompleted )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_FrameNavigationCompleted )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ScriptDialogOpening )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2ScriptDialogOpeningEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ScriptDialogOpening )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_PermissionRequested )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2PermissionRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_PermissionRequested )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ProcessFailed )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2ProcessFailedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ProcessFailed )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *AddScriptToExecuteOnDocumentCreated )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *RemoveScriptToExecuteOnDocumentCreated )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR id); + + HRESULT ( STDMETHODCALLTYPE *ExecuteScript )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2ExecuteScriptCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CapturePreview )( + ICoreWebView2 * This, + /* [in] */ COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT imageFormat, + /* [in] */ IStream *imageStream, + /* [in] */ ICoreWebView2CapturePreviewCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *Reload )( + ICoreWebView2 * This); + + HRESULT ( STDMETHODCALLTYPE *PostWebMessageAsJson )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR webMessageAsJson); + + HRESULT ( STDMETHODCALLTYPE *PostWebMessageAsString )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR webMessageAsString); + + HRESULT ( STDMETHODCALLTYPE *add_WebMessageReceived )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2WebMessageReceivedEventHandler *handler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebMessageReceived )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *CallDevToolsProtocolMethod )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR methodName, + /* [in] */ LPCWSTR parametersAsJson, + /* [in] */ ICoreWebView2CallDevToolsProtocolMethodCompletedHandler *handler); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BrowserProcessId )( + ICoreWebView2 * This, + /* [retval][out] */ UINT32 *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CanGoBack )( + ICoreWebView2 * This, + /* [retval][out] */ BOOL *canGoBack); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CanGoForward )( + ICoreWebView2 * This, + /* [retval][out] */ BOOL *canGoForward); + + HRESULT ( STDMETHODCALLTYPE *GoBack )( + ICoreWebView2 * This); + + HRESULT ( STDMETHODCALLTYPE *GoForward )( + ICoreWebView2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetDevToolsProtocolEventReceiver )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR eventName, + /* [retval][out] */ ICoreWebView2DevToolsProtocolEventReceiver **receiver); + + HRESULT ( STDMETHODCALLTYPE *Stop )( + ICoreWebView2 * This); + + HRESULT ( STDMETHODCALLTYPE *add_NewWindowRequested )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2NewWindowRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NewWindowRequested )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_DocumentTitleChanged )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2DocumentTitleChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_DocumentTitleChanged )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DocumentTitle )( + ICoreWebView2 * This, + /* [retval][out] */ LPWSTR *title); + + HRESULT ( STDMETHODCALLTYPE *AddHostObjectToScript )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR name, + /* [in] */ VARIANT *object); + + HRESULT ( STDMETHODCALLTYPE *RemoveHostObjectFromScript )( + ICoreWebView2 * This, + /* [in] */ LPCWSTR name); + + HRESULT ( STDMETHODCALLTYPE *OpenDevToolsWindow )( + ICoreWebView2 * This); + + HRESULT ( STDMETHODCALLTYPE *add_ContainsFullScreenElementChanged )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2ContainsFullScreenElementChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ContainsFullScreenElementChanged )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ContainsFullScreenElement )( + ICoreWebView2 * This, + /* [retval][out] */ BOOL *containsFullScreenElement); + + HRESULT ( STDMETHODCALLTYPE *add_WebResourceRequested )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2WebResourceRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebResourceRequested )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *AddWebResourceRequestedFilter )( + ICoreWebView2 * This, + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext); + + HRESULT ( STDMETHODCALLTYPE *RemoveWebResourceRequestedFilter )( + ICoreWebView2 * This, + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext); + + HRESULT ( STDMETHODCALLTYPE *add_WindowCloseRequested )( + ICoreWebView2 * This, + /* [in] */ ICoreWebView2WindowCloseRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WindowCloseRequested )( + ICoreWebView2 * This, + /* [in] */ EventRegistrationToken token); + + END_INTERFACE + } ICoreWebView2Vtbl; + + interface ICoreWebView2 + { + CONST_VTBL struct ICoreWebView2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2_get_Settings(This,settings) \ + ( (This)->lpVtbl -> get_Settings(This,settings) ) + +#define ICoreWebView2_get_Source(This,uri) \ + ( (This)->lpVtbl -> get_Source(This,uri) ) + +#define ICoreWebView2_Navigate(This,uri) \ + ( (This)->lpVtbl -> Navigate(This,uri) ) + +#define ICoreWebView2_NavigateToString(This,htmlContent) \ + ( (This)->lpVtbl -> NavigateToString(This,htmlContent) ) + +#define ICoreWebView2_add_NavigationStarting(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NavigationStarting(This,eventHandler,token) ) + +#define ICoreWebView2_remove_NavigationStarting(This,token) \ + ( (This)->lpVtbl -> remove_NavigationStarting(This,token) ) + +#define ICoreWebView2_add_ContentLoading(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ContentLoading(This,eventHandler,token) ) + +#define ICoreWebView2_remove_ContentLoading(This,token) \ + ( (This)->lpVtbl -> remove_ContentLoading(This,token) ) + +#define ICoreWebView2_add_SourceChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_SourceChanged(This,eventHandler,token) ) + +#define ICoreWebView2_remove_SourceChanged(This,token) \ + ( (This)->lpVtbl -> remove_SourceChanged(This,token) ) + +#define ICoreWebView2_add_HistoryChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_HistoryChanged(This,eventHandler,token) ) + +#define ICoreWebView2_remove_HistoryChanged(This,token) \ + ( (This)->lpVtbl -> remove_HistoryChanged(This,token) ) + +#define ICoreWebView2_add_NavigationCompleted(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NavigationCompleted(This,eventHandler,token) ) + +#define ICoreWebView2_remove_NavigationCompleted(This,token) \ + ( (This)->lpVtbl -> remove_NavigationCompleted(This,token) ) + +#define ICoreWebView2_add_FrameNavigationStarting(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_FrameNavigationStarting(This,eventHandler,token) ) + +#define ICoreWebView2_remove_FrameNavigationStarting(This,token) \ + ( (This)->lpVtbl -> remove_FrameNavigationStarting(This,token) ) + +#define ICoreWebView2_add_FrameNavigationCompleted(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_FrameNavigationCompleted(This,eventHandler,token) ) + +#define ICoreWebView2_remove_FrameNavigationCompleted(This,token) \ + ( (This)->lpVtbl -> remove_FrameNavigationCompleted(This,token) ) + +#define ICoreWebView2_add_ScriptDialogOpening(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ScriptDialogOpening(This,eventHandler,token) ) + +#define ICoreWebView2_remove_ScriptDialogOpening(This,token) \ + ( (This)->lpVtbl -> remove_ScriptDialogOpening(This,token) ) + +#define ICoreWebView2_add_PermissionRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_PermissionRequested(This,eventHandler,token) ) + +#define ICoreWebView2_remove_PermissionRequested(This,token) \ + ( (This)->lpVtbl -> remove_PermissionRequested(This,token) ) + +#define ICoreWebView2_add_ProcessFailed(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ProcessFailed(This,eventHandler,token) ) + +#define ICoreWebView2_remove_ProcessFailed(This,token) \ + ( (This)->lpVtbl -> remove_ProcessFailed(This,token) ) + +#define ICoreWebView2_AddScriptToExecuteOnDocumentCreated(This,javaScript,handler) \ + ( (This)->lpVtbl -> AddScriptToExecuteOnDocumentCreated(This,javaScript,handler) ) + +#define ICoreWebView2_RemoveScriptToExecuteOnDocumentCreated(This,id) \ + ( (This)->lpVtbl -> RemoveScriptToExecuteOnDocumentCreated(This,id) ) + +#define ICoreWebView2_ExecuteScript(This,javaScript,handler) \ + ( (This)->lpVtbl -> ExecuteScript(This,javaScript,handler) ) + +#define ICoreWebView2_CapturePreview(This,imageFormat,imageStream,handler) \ + ( (This)->lpVtbl -> CapturePreview(This,imageFormat,imageStream,handler) ) + +#define ICoreWebView2_Reload(This) \ + ( (This)->lpVtbl -> Reload(This) ) + +#define ICoreWebView2_PostWebMessageAsJson(This,webMessageAsJson) \ + ( (This)->lpVtbl -> PostWebMessageAsJson(This,webMessageAsJson) ) + +#define ICoreWebView2_PostWebMessageAsString(This,webMessageAsString) \ + ( (This)->lpVtbl -> PostWebMessageAsString(This,webMessageAsString) ) + +#define ICoreWebView2_add_WebMessageReceived(This,handler,token) \ + ( (This)->lpVtbl -> add_WebMessageReceived(This,handler,token) ) + +#define ICoreWebView2_remove_WebMessageReceived(This,token) \ + ( (This)->lpVtbl -> remove_WebMessageReceived(This,token) ) + +#define ICoreWebView2_CallDevToolsProtocolMethod(This,methodName,parametersAsJson,handler) \ + ( (This)->lpVtbl -> CallDevToolsProtocolMethod(This,methodName,parametersAsJson,handler) ) + +#define ICoreWebView2_get_BrowserProcessId(This,value) \ + ( (This)->lpVtbl -> get_BrowserProcessId(This,value) ) + +#define ICoreWebView2_get_CanGoBack(This,canGoBack) \ + ( (This)->lpVtbl -> get_CanGoBack(This,canGoBack) ) + +#define ICoreWebView2_get_CanGoForward(This,canGoForward) \ + ( (This)->lpVtbl -> get_CanGoForward(This,canGoForward) ) + +#define ICoreWebView2_GoBack(This) \ + ( (This)->lpVtbl -> GoBack(This) ) + +#define ICoreWebView2_GoForward(This) \ + ( (This)->lpVtbl -> GoForward(This) ) + +#define ICoreWebView2_GetDevToolsProtocolEventReceiver(This,eventName,receiver) \ + ( (This)->lpVtbl -> GetDevToolsProtocolEventReceiver(This,eventName,receiver) ) + +#define ICoreWebView2_Stop(This) \ + ( (This)->lpVtbl -> Stop(This) ) + +#define ICoreWebView2_add_NewWindowRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NewWindowRequested(This,eventHandler,token) ) + +#define ICoreWebView2_remove_NewWindowRequested(This,token) \ + ( (This)->lpVtbl -> remove_NewWindowRequested(This,token) ) + +#define ICoreWebView2_add_DocumentTitleChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_DocumentTitleChanged(This,eventHandler,token) ) + +#define ICoreWebView2_remove_DocumentTitleChanged(This,token) \ + ( (This)->lpVtbl -> remove_DocumentTitleChanged(This,token) ) + +#define ICoreWebView2_get_DocumentTitle(This,title) \ + ( (This)->lpVtbl -> get_DocumentTitle(This,title) ) + +#define ICoreWebView2_AddHostObjectToScript(This,name,object) \ + ( (This)->lpVtbl -> AddHostObjectToScript(This,name,object) ) + +#define ICoreWebView2_RemoveHostObjectFromScript(This,name) \ + ( (This)->lpVtbl -> RemoveHostObjectFromScript(This,name) ) + +#define ICoreWebView2_OpenDevToolsWindow(This) \ + ( (This)->lpVtbl -> OpenDevToolsWindow(This) ) + +#define ICoreWebView2_add_ContainsFullScreenElementChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ContainsFullScreenElementChanged(This,eventHandler,token) ) + +#define ICoreWebView2_remove_ContainsFullScreenElementChanged(This,token) \ + ( (This)->lpVtbl -> remove_ContainsFullScreenElementChanged(This,token) ) + +#define ICoreWebView2_get_ContainsFullScreenElement(This,containsFullScreenElement) \ + ( (This)->lpVtbl -> get_ContainsFullScreenElement(This,containsFullScreenElement) ) + +#define ICoreWebView2_add_WebResourceRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WebResourceRequested(This,eventHandler,token) ) + +#define ICoreWebView2_remove_WebResourceRequested(This,token) \ + ( (This)->lpVtbl -> remove_WebResourceRequested(This,token) ) + +#define ICoreWebView2_AddWebResourceRequestedFilter(This,uri,resourceContext) \ + ( (This)->lpVtbl -> AddWebResourceRequestedFilter(This,uri,resourceContext) ) + +#define ICoreWebView2_RemoveWebResourceRequestedFilter(This,uri,resourceContext) \ + ( (This)->lpVtbl -> RemoveWebResourceRequestedFilter(This,uri,resourceContext) ) + +#define ICoreWebView2_add_WindowCloseRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WindowCloseRequested(This,eventHandler,token) ) + +#define ICoreWebView2_remove_WindowCloseRequested(This,token) \ + ( (This)->lpVtbl -> remove_WindowCloseRequested(This,token) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2_2_INTERFACE_DEFINED__ +#define __ICoreWebView2_2_INTERFACE_DEFINED__ + +/* interface ICoreWebView2_2 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2_2 = {0x9E8F0CF8,0xE670,0x4B5E,{0xB2,0xBC,0x73,0xE0,0x61,0xE3,0x18,0x4C}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9E8F0CF8-E670-4B5E-B2BC-73E061E3184C") + ICoreWebView2_2 : public ICoreWebView2 + { + public: + virtual HRESULT STDMETHODCALLTYPE add_WebResourceResponseReceived( + /* [in] */ ICoreWebView2WebResourceResponseReceivedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_WebResourceResponseReceived( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE NavigateWithWebResourceRequest( + /* [in] */ ICoreWebView2WebResourceRequest *request) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_DOMContentLoaded( + /* [in] */ ICoreWebView2DOMContentLoadedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_DOMContentLoaded( + /* [in] */ EventRegistrationToken token) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CookieManager( + /* [retval][out] */ ICoreWebView2CookieManager **cookieManager) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Environment( + /* [retval][out] */ ICoreWebView2Environment **environment) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2_2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2_2 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2_2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2_2 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Settings )( + ICoreWebView2_2 * This, + /* [retval][out] */ ICoreWebView2Settings **settings); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Source )( + ICoreWebView2_2 * This, + /* [retval][out] */ LPWSTR *uri); + + HRESULT ( STDMETHODCALLTYPE *Navigate )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR uri); + + HRESULT ( STDMETHODCALLTYPE *NavigateToString )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR htmlContent); + + HRESULT ( STDMETHODCALLTYPE *add_NavigationStarting )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NavigationStarting )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ContentLoading )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2ContentLoadingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ContentLoading )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_SourceChanged )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2SourceChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_SourceChanged )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_HistoryChanged )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2HistoryChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_HistoryChanged )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_NavigationCompleted )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NavigationCompleted )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_FrameNavigationStarting )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_FrameNavigationStarting )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_FrameNavigationCompleted )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_FrameNavigationCompleted )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ScriptDialogOpening )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2ScriptDialogOpeningEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ScriptDialogOpening )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_PermissionRequested )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2PermissionRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_PermissionRequested )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ProcessFailed )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2ProcessFailedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ProcessFailed )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *AddScriptToExecuteOnDocumentCreated )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *RemoveScriptToExecuteOnDocumentCreated )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR id); + + HRESULT ( STDMETHODCALLTYPE *ExecuteScript )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2ExecuteScriptCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CapturePreview )( + ICoreWebView2_2 * This, + /* [in] */ COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT imageFormat, + /* [in] */ IStream *imageStream, + /* [in] */ ICoreWebView2CapturePreviewCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *Reload )( + ICoreWebView2_2 * This); + + HRESULT ( STDMETHODCALLTYPE *PostWebMessageAsJson )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR webMessageAsJson); + + HRESULT ( STDMETHODCALLTYPE *PostWebMessageAsString )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR webMessageAsString); + + HRESULT ( STDMETHODCALLTYPE *add_WebMessageReceived )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2WebMessageReceivedEventHandler *handler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebMessageReceived )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *CallDevToolsProtocolMethod )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR methodName, + /* [in] */ LPCWSTR parametersAsJson, + /* [in] */ ICoreWebView2CallDevToolsProtocolMethodCompletedHandler *handler); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BrowserProcessId )( + ICoreWebView2_2 * This, + /* [retval][out] */ UINT32 *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CanGoBack )( + ICoreWebView2_2 * This, + /* [retval][out] */ BOOL *canGoBack); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CanGoForward )( + ICoreWebView2_2 * This, + /* [retval][out] */ BOOL *canGoForward); + + HRESULT ( STDMETHODCALLTYPE *GoBack )( + ICoreWebView2_2 * This); + + HRESULT ( STDMETHODCALLTYPE *GoForward )( + ICoreWebView2_2 * This); + + HRESULT ( STDMETHODCALLTYPE *GetDevToolsProtocolEventReceiver )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR eventName, + /* [retval][out] */ ICoreWebView2DevToolsProtocolEventReceiver **receiver); + + HRESULT ( STDMETHODCALLTYPE *Stop )( + ICoreWebView2_2 * This); + + HRESULT ( STDMETHODCALLTYPE *add_NewWindowRequested )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2NewWindowRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NewWindowRequested )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_DocumentTitleChanged )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2DocumentTitleChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_DocumentTitleChanged )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DocumentTitle )( + ICoreWebView2_2 * This, + /* [retval][out] */ LPWSTR *title); + + HRESULT ( STDMETHODCALLTYPE *AddHostObjectToScript )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR name, + /* [in] */ VARIANT *object); + + HRESULT ( STDMETHODCALLTYPE *RemoveHostObjectFromScript )( + ICoreWebView2_2 * This, + /* [in] */ LPCWSTR name); + + HRESULT ( STDMETHODCALLTYPE *OpenDevToolsWindow )( + ICoreWebView2_2 * This); + + HRESULT ( STDMETHODCALLTYPE *add_ContainsFullScreenElementChanged )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2ContainsFullScreenElementChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ContainsFullScreenElementChanged )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ContainsFullScreenElement )( + ICoreWebView2_2 * This, + /* [retval][out] */ BOOL *containsFullScreenElement); + + HRESULT ( STDMETHODCALLTYPE *add_WebResourceRequested )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2WebResourceRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebResourceRequested )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *AddWebResourceRequestedFilter )( + ICoreWebView2_2 * This, + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext); + + HRESULT ( STDMETHODCALLTYPE *RemoveWebResourceRequestedFilter )( + ICoreWebView2_2 * This, + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext); + + HRESULT ( STDMETHODCALLTYPE *add_WindowCloseRequested )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2WindowCloseRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WindowCloseRequested )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_WebResourceResponseReceived )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2WebResourceResponseReceivedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebResourceResponseReceived )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *NavigateWithWebResourceRequest )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2WebResourceRequest *request); + + HRESULT ( STDMETHODCALLTYPE *add_DOMContentLoaded )( + ICoreWebView2_2 * This, + /* [in] */ ICoreWebView2DOMContentLoadedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_DOMContentLoaded )( + ICoreWebView2_2 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CookieManager )( + ICoreWebView2_2 * This, + /* [retval][out] */ ICoreWebView2CookieManager **cookieManager); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Environment )( + ICoreWebView2_2 * This, + /* [retval][out] */ ICoreWebView2Environment **environment); + + END_INTERFACE + } ICoreWebView2_2Vtbl; + + interface ICoreWebView2_2 + { + CONST_VTBL struct ICoreWebView2_2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2_2_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2_2_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2_2_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2_2_get_Settings(This,settings) \ + ( (This)->lpVtbl -> get_Settings(This,settings) ) + +#define ICoreWebView2_2_get_Source(This,uri) \ + ( (This)->lpVtbl -> get_Source(This,uri) ) + +#define ICoreWebView2_2_Navigate(This,uri) \ + ( (This)->lpVtbl -> Navigate(This,uri) ) + +#define ICoreWebView2_2_NavigateToString(This,htmlContent) \ + ( (This)->lpVtbl -> NavigateToString(This,htmlContent) ) + +#define ICoreWebView2_2_add_NavigationStarting(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NavigationStarting(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_NavigationStarting(This,token) \ + ( (This)->lpVtbl -> remove_NavigationStarting(This,token) ) + +#define ICoreWebView2_2_add_ContentLoading(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ContentLoading(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_ContentLoading(This,token) \ + ( (This)->lpVtbl -> remove_ContentLoading(This,token) ) + +#define ICoreWebView2_2_add_SourceChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_SourceChanged(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_SourceChanged(This,token) \ + ( (This)->lpVtbl -> remove_SourceChanged(This,token) ) + +#define ICoreWebView2_2_add_HistoryChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_HistoryChanged(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_HistoryChanged(This,token) \ + ( (This)->lpVtbl -> remove_HistoryChanged(This,token) ) + +#define ICoreWebView2_2_add_NavigationCompleted(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NavigationCompleted(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_NavigationCompleted(This,token) \ + ( (This)->lpVtbl -> remove_NavigationCompleted(This,token) ) + +#define ICoreWebView2_2_add_FrameNavigationStarting(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_FrameNavigationStarting(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_FrameNavigationStarting(This,token) \ + ( (This)->lpVtbl -> remove_FrameNavigationStarting(This,token) ) + +#define ICoreWebView2_2_add_FrameNavigationCompleted(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_FrameNavigationCompleted(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_FrameNavigationCompleted(This,token) \ + ( (This)->lpVtbl -> remove_FrameNavigationCompleted(This,token) ) + +#define ICoreWebView2_2_add_ScriptDialogOpening(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ScriptDialogOpening(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_ScriptDialogOpening(This,token) \ + ( (This)->lpVtbl -> remove_ScriptDialogOpening(This,token) ) + +#define ICoreWebView2_2_add_PermissionRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_PermissionRequested(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_PermissionRequested(This,token) \ + ( (This)->lpVtbl -> remove_PermissionRequested(This,token) ) + +#define ICoreWebView2_2_add_ProcessFailed(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ProcessFailed(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_ProcessFailed(This,token) \ + ( (This)->lpVtbl -> remove_ProcessFailed(This,token) ) + +#define ICoreWebView2_2_AddScriptToExecuteOnDocumentCreated(This,javaScript,handler) \ + ( (This)->lpVtbl -> AddScriptToExecuteOnDocumentCreated(This,javaScript,handler) ) + +#define ICoreWebView2_2_RemoveScriptToExecuteOnDocumentCreated(This,id) \ + ( (This)->lpVtbl -> RemoveScriptToExecuteOnDocumentCreated(This,id) ) + +#define ICoreWebView2_2_ExecuteScript(This,javaScript,handler) \ + ( (This)->lpVtbl -> ExecuteScript(This,javaScript,handler) ) + +#define ICoreWebView2_2_CapturePreview(This,imageFormat,imageStream,handler) \ + ( (This)->lpVtbl -> CapturePreview(This,imageFormat,imageStream,handler) ) + +#define ICoreWebView2_2_Reload(This) \ + ( (This)->lpVtbl -> Reload(This) ) + +#define ICoreWebView2_2_PostWebMessageAsJson(This,webMessageAsJson) \ + ( (This)->lpVtbl -> PostWebMessageAsJson(This,webMessageAsJson) ) + +#define ICoreWebView2_2_PostWebMessageAsString(This,webMessageAsString) \ + ( (This)->lpVtbl -> PostWebMessageAsString(This,webMessageAsString) ) + +#define ICoreWebView2_2_add_WebMessageReceived(This,handler,token) \ + ( (This)->lpVtbl -> add_WebMessageReceived(This,handler,token) ) + +#define ICoreWebView2_2_remove_WebMessageReceived(This,token) \ + ( (This)->lpVtbl -> remove_WebMessageReceived(This,token) ) + +#define ICoreWebView2_2_CallDevToolsProtocolMethod(This,methodName,parametersAsJson,handler) \ + ( (This)->lpVtbl -> CallDevToolsProtocolMethod(This,methodName,parametersAsJson,handler) ) + +#define ICoreWebView2_2_get_BrowserProcessId(This,value) \ + ( (This)->lpVtbl -> get_BrowserProcessId(This,value) ) + +#define ICoreWebView2_2_get_CanGoBack(This,canGoBack) \ + ( (This)->lpVtbl -> get_CanGoBack(This,canGoBack) ) + +#define ICoreWebView2_2_get_CanGoForward(This,canGoForward) \ + ( (This)->lpVtbl -> get_CanGoForward(This,canGoForward) ) + +#define ICoreWebView2_2_GoBack(This) \ + ( (This)->lpVtbl -> GoBack(This) ) + +#define ICoreWebView2_2_GoForward(This) \ + ( (This)->lpVtbl -> GoForward(This) ) + +#define ICoreWebView2_2_GetDevToolsProtocolEventReceiver(This,eventName,receiver) \ + ( (This)->lpVtbl -> GetDevToolsProtocolEventReceiver(This,eventName,receiver) ) + +#define ICoreWebView2_2_Stop(This) \ + ( (This)->lpVtbl -> Stop(This) ) + +#define ICoreWebView2_2_add_NewWindowRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NewWindowRequested(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_NewWindowRequested(This,token) \ + ( (This)->lpVtbl -> remove_NewWindowRequested(This,token) ) + +#define ICoreWebView2_2_add_DocumentTitleChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_DocumentTitleChanged(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_DocumentTitleChanged(This,token) \ + ( (This)->lpVtbl -> remove_DocumentTitleChanged(This,token) ) + +#define ICoreWebView2_2_get_DocumentTitle(This,title) \ + ( (This)->lpVtbl -> get_DocumentTitle(This,title) ) + +#define ICoreWebView2_2_AddHostObjectToScript(This,name,object) \ + ( (This)->lpVtbl -> AddHostObjectToScript(This,name,object) ) + +#define ICoreWebView2_2_RemoveHostObjectFromScript(This,name) \ + ( (This)->lpVtbl -> RemoveHostObjectFromScript(This,name) ) + +#define ICoreWebView2_2_OpenDevToolsWindow(This) \ + ( (This)->lpVtbl -> OpenDevToolsWindow(This) ) + +#define ICoreWebView2_2_add_ContainsFullScreenElementChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ContainsFullScreenElementChanged(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_ContainsFullScreenElementChanged(This,token) \ + ( (This)->lpVtbl -> remove_ContainsFullScreenElementChanged(This,token) ) + +#define ICoreWebView2_2_get_ContainsFullScreenElement(This,containsFullScreenElement) \ + ( (This)->lpVtbl -> get_ContainsFullScreenElement(This,containsFullScreenElement) ) + +#define ICoreWebView2_2_add_WebResourceRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WebResourceRequested(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_WebResourceRequested(This,token) \ + ( (This)->lpVtbl -> remove_WebResourceRequested(This,token) ) + +#define ICoreWebView2_2_AddWebResourceRequestedFilter(This,uri,resourceContext) \ + ( (This)->lpVtbl -> AddWebResourceRequestedFilter(This,uri,resourceContext) ) + +#define ICoreWebView2_2_RemoveWebResourceRequestedFilter(This,uri,resourceContext) \ + ( (This)->lpVtbl -> RemoveWebResourceRequestedFilter(This,uri,resourceContext) ) + +#define ICoreWebView2_2_add_WindowCloseRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WindowCloseRequested(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_WindowCloseRequested(This,token) \ + ( (This)->lpVtbl -> remove_WindowCloseRequested(This,token) ) + + +#define ICoreWebView2_2_add_WebResourceResponseReceived(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WebResourceResponseReceived(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_WebResourceResponseReceived(This,token) \ + ( (This)->lpVtbl -> remove_WebResourceResponseReceived(This,token) ) + +#define ICoreWebView2_2_NavigateWithWebResourceRequest(This,request) \ + ( (This)->lpVtbl -> NavigateWithWebResourceRequest(This,request) ) + +#define ICoreWebView2_2_add_DOMContentLoaded(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_DOMContentLoaded(This,eventHandler,token) ) + +#define ICoreWebView2_2_remove_DOMContentLoaded(This,token) \ + ( (This)->lpVtbl -> remove_DOMContentLoaded(This,token) ) + +#define ICoreWebView2_2_get_CookieManager(This,cookieManager) \ + ( (This)->lpVtbl -> get_CookieManager(This,cookieManager) ) + +#define ICoreWebView2_2_get_Environment(This,environment) \ + ( (This)->lpVtbl -> get_Environment(This,environment) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2_2_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2_3_INTERFACE_DEFINED__ +#define __ICoreWebView2_3_INTERFACE_DEFINED__ + +/* interface ICoreWebView2_3 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2_3 = {0xA0D6DF20,0x3B92,0x416D,{0xAA,0x0C,0x43,0x7A,0x9C,0x72,0x78,0x57}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("A0D6DF20-3B92-416D-AA0C-437A9C727857") + ICoreWebView2_3 : public ICoreWebView2_2 + { + public: + virtual HRESULT STDMETHODCALLTYPE TrySuspend( + /* [in] */ ICoreWebView2TrySuspendCompletedHandler *handler) = 0; + + virtual HRESULT STDMETHODCALLTYPE Resume( void) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsSuspended( + /* [retval][out] */ BOOL *isSuspended) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetVirtualHostNameToFolderMapping( + /* [in] */ LPCWSTR hostName, + /* [in] */ LPCWSTR folderPath, + /* [in] */ COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND accessKind) = 0; + + virtual HRESULT STDMETHODCALLTYPE ClearVirtualHostNameToFolderMapping( + /* [in] */ LPCWSTR hostName) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2_3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2_3 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2_3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2_3 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Settings )( + ICoreWebView2_3 * This, + /* [retval][out] */ ICoreWebView2Settings **settings); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Source )( + ICoreWebView2_3 * This, + /* [retval][out] */ LPWSTR *uri); + + HRESULT ( STDMETHODCALLTYPE *Navigate )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR uri); + + HRESULT ( STDMETHODCALLTYPE *NavigateToString )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR htmlContent); + + HRESULT ( STDMETHODCALLTYPE *add_NavigationStarting )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NavigationStarting )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ContentLoading )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2ContentLoadingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ContentLoading )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_SourceChanged )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2SourceChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_SourceChanged )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_HistoryChanged )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2HistoryChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_HistoryChanged )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_NavigationCompleted )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NavigationCompleted )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_FrameNavigationStarting )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2NavigationStartingEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_FrameNavigationStarting )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_FrameNavigationCompleted )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2NavigationCompletedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_FrameNavigationCompleted )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ScriptDialogOpening )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2ScriptDialogOpeningEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ScriptDialogOpening )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_PermissionRequested )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2PermissionRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_PermissionRequested )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_ProcessFailed )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2ProcessFailedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ProcessFailed )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *AddScriptToExecuteOnDocumentCreated )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *RemoveScriptToExecuteOnDocumentCreated )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR id); + + HRESULT ( STDMETHODCALLTYPE *ExecuteScript )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR javaScript, + /* [in] */ ICoreWebView2ExecuteScriptCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CapturePreview )( + ICoreWebView2_3 * This, + /* [in] */ COREWEBVIEW2_CAPTURE_PREVIEW_IMAGE_FORMAT imageFormat, + /* [in] */ IStream *imageStream, + /* [in] */ ICoreWebView2CapturePreviewCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *Reload )( + ICoreWebView2_3 * This); + + HRESULT ( STDMETHODCALLTYPE *PostWebMessageAsJson )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR webMessageAsJson); + + HRESULT ( STDMETHODCALLTYPE *PostWebMessageAsString )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR webMessageAsString); + + HRESULT ( STDMETHODCALLTYPE *add_WebMessageReceived )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2WebMessageReceivedEventHandler *handler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebMessageReceived )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *CallDevToolsProtocolMethod )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR methodName, + /* [in] */ LPCWSTR parametersAsJson, + /* [in] */ ICoreWebView2CallDevToolsProtocolMethodCompletedHandler *handler); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BrowserProcessId )( + ICoreWebView2_3 * This, + /* [retval][out] */ UINT32 *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CanGoBack )( + ICoreWebView2_3 * This, + /* [retval][out] */ BOOL *canGoBack); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CanGoForward )( + ICoreWebView2_3 * This, + /* [retval][out] */ BOOL *canGoForward); + + HRESULT ( STDMETHODCALLTYPE *GoBack )( + ICoreWebView2_3 * This); + + HRESULT ( STDMETHODCALLTYPE *GoForward )( + ICoreWebView2_3 * This); + + HRESULT ( STDMETHODCALLTYPE *GetDevToolsProtocolEventReceiver )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR eventName, + /* [retval][out] */ ICoreWebView2DevToolsProtocolEventReceiver **receiver); + + HRESULT ( STDMETHODCALLTYPE *Stop )( + ICoreWebView2_3 * This); + + HRESULT ( STDMETHODCALLTYPE *add_NewWindowRequested )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2NewWindowRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NewWindowRequested )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_DocumentTitleChanged )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2DocumentTitleChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_DocumentTitleChanged )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DocumentTitle )( + ICoreWebView2_3 * This, + /* [retval][out] */ LPWSTR *title); + + HRESULT ( STDMETHODCALLTYPE *AddHostObjectToScript )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR name, + /* [in] */ VARIANT *object); + + HRESULT ( STDMETHODCALLTYPE *RemoveHostObjectFromScript )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR name); + + HRESULT ( STDMETHODCALLTYPE *OpenDevToolsWindow )( + ICoreWebView2_3 * This); + + HRESULT ( STDMETHODCALLTYPE *add_ContainsFullScreenElementChanged )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2ContainsFullScreenElementChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ContainsFullScreenElementChanged )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ContainsFullScreenElement )( + ICoreWebView2_3 * This, + /* [retval][out] */ BOOL *containsFullScreenElement); + + HRESULT ( STDMETHODCALLTYPE *add_WebResourceRequested )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2WebResourceRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebResourceRequested )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *AddWebResourceRequestedFilter )( + ICoreWebView2_3 * This, + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext); + + HRESULT ( STDMETHODCALLTYPE *RemoveWebResourceRequestedFilter )( + ICoreWebView2_3 * This, + /* [in] */ const LPCWSTR uri, + /* [in] */ const COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext); + + HRESULT ( STDMETHODCALLTYPE *add_WindowCloseRequested )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2WindowCloseRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WindowCloseRequested )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_WebResourceResponseReceived )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2WebResourceResponseReceivedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_WebResourceResponseReceived )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *NavigateWithWebResourceRequest )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2WebResourceRequest *request); + + HRESULT ( STDMETHODCALLTYPE *add_DOMContentLoaded )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2DOMContentLoadedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_DOMContentLoaded )( + ICoreWebView2_3 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CookieManager )( + ICoreWebView2_3 * This, + /* [retval][out] */ ICoreWebView2CookieManager **cookieManager); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Environment )( + ICoreWebView2_3 * This, + /* [retval][out] */ ICoreWebView2Environment **environment); + + HRESULT ( STDMETHODCALLTYPE *TrySuspend )( + ICoreWebView2_3 * This, + /* [in] */ ICoreWebView2TrySuspendCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *Resume )( + ICoreWebView2_3 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsSuspended )( + ICoreWebView2_3 * This, + /* [retval][out] */ BOOL *isSuspended); + + HRESULT ( STDMETHODCALLTYPE *SetVirtualHostNameToFolderMapping )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR hostName, + /* [in] */ LPCWSTR folderPath, + /* [in] */ COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND accessKind); + + HRESULT ( STDMETHODCALLTYPE *ClearVirtualHostNameToFolderMapping )( + ICoreWebView2_3 * This, + /* [in] */ LPCWSTR hostName); + + END_INTERFACE + } ICoreWebView2_3Vtbl; + + interface ICoreWebView2_3 + { + CONST_VTBL struct ICoreWebView2_3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2_3_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2_3_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2_3_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2_3_get_Settings(This,settings) \ + ( (This)->lpVtbl -> get_Settings(This,settings) ) + +#define ICoreWebView2_3_get_Source(This,uri) \ + ( (This)->lpVtbl -> get_Source(This,uri) ) + +#define ICoreWebView2_3_Navigate(This,uri) \ + ( (This)->lpVtbl -> Navigate(This,uri) ) + +#define ICoreWebView2_3_NavigateToString(This,htmlContent) \ + ( (This)->lpVtbl -> NavigateToString(This,htmlContent) ) + +#define ICoreWebView2_3_add_NavigationStarting(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NavigationStarting(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_NavigationStarting(This,token) \ + ( (This)->lpVtbl -> remove_NavigationStarting(This,token) ) + +#define ICoreWebView2_3_add_ContentLoading(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ContentLoading(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_ContentLoading(This,token) \ + ( (This)->lpVtbl -> remove_ContentLoading(This,token) ) + +#define ICoreWebView2_3_add_SourceChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_SourceChanged(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_SourceChanged(This,token) \ + ( (This)->lpVtbl -> remove_SourceChanged(This,token) ) + +#define ICoreWebView2_3_add_HistoryChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_HistoryChanged(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_HistoryChanged(This,token) \ + ( (This)->lpVtbl -> remove_HistoryChanged(This,token) ) + +#define ICoreWebView2_3_add_NavigationCompleted(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NavigationCompleted(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_NavigationCompleted(This,token) \ + ( (This)->lpVtbl -> remove_NavigationCompleted(This,token) ) + +#define ICoreWebView2_3_add_FrameNavigationStarting(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_FrameNavigationStarting(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_FrameNavigationStarting(This,token) \ + ( (This)->lpVtbl -> remove_FrameNavigationStarting(This,token) ) + +#define ICoreWebView2_3_add_FrameNavigationCompleted(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_FrameNavigationCompleted(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_FrameNavigationCompleted(This,token) \ + ( (This)->lpVtbl -> remove_FrameNavigationCompleted(This,token) ) + +#define ICoreWebView2_3_add_ScriptDialogOpening(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ScriptDialogOpening(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_ScriptDialogOpening(This,token) \ + ( (This)->lpVtbl -> remove_ScriptDialogOpening(This,token) ) + +#define ICoreWebView2_3_add_PermissionRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_PermissionRequested(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_PermissionRequested(This,token) \ + ( (This)->lpVtbl -> remove_PermissionRequested(This,token) ) + +#define ICoreWebView2_3_add_ProcessFailed(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ProcessFailed(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_ProcessFailed(This,token) \ + ( (This)->lpVtbl -> remove_ProcessFailed(This,token) ) + +#define ICoreWebView2_3_AddScriptToExecuteOnDocumentCreated(This,javaScript,handler) \ + ( (This)->lpVtbl -> AddScriptToExecuteOnDocumentCreated(This,javaScript,handler) ) + +#define ICoreWebView2_3_RemoveScriptToExecuteOnDocumentCreated(This,id) \ + ( (This)->lpVtbl -> RemoveScriptToExecuteOnDocumentCreated(This,id) ) + +#define ICoreWebView2_3_ExecuteScript(This,javaScript,handler) \ + ( (This)->lpVtbl -> ExecuteScript(This,javaScript,handler) ) + +#define ICoreWebView2_3_CapturePreview(This,imageFormat,imageStream,handler) \ + ( (This)->lpVtbl -> CapturePreview(This,imageFormat,imageStream,handler) ) + +#define ICoreWebView2_3_Reload(This) \ + ( (This)->lpVtbl -> Reload(This) ) + +#define ICoreWebView2_3_PostWebMessageAsJson(This,webMessageAsJson) \ + ( (This)->lpVtbl -> PostWebMessageAsJson(This,webMessageAsJson) ) + +#define ICoreWebView2_3_PostWebMessageAsString(This,webMessageAsString) \ + ( (This)->lpVtbl -> PostWebMessageAsString(This,webMessageAsString) ) + +#define ICoreWebView2_3_add_WebMessageReceived(This,handler,token) \ + ( (This)->lpVtbl -> add_WebMessageReceived(This,handler,token) ) + +#define ICoreWebView2_3_remove_WebMessageReceived(This,token) \ + ( (This)->lpVtbl -> remove_WebMessageReceived(This,token) ) + +#define ICoreWebView2_3_CallDevToolsProtocolMethod(This,methodName,parametersAsJson,handler) \ + ( (This)->lpVtbl -> CallDevToolsProtocolMethod(This,methodName,parametersAsJson,handler) ) + +#define ICoreWebView2_3_get_BrowserProcessId(This,value) \ + ( (This)->lpVtbl -> get_BrowserProcessId(This,value) ) + +#define ICoreWebView2_3_get_CanGoBack(This,canGoBack) \ + ( (This)->lpVtbl -> get_CanGoBack(This,canGoBack) ) + +#define ICoreWebView2_3_get_CanGoForward(This,canGoForward) \ + ( (This)->lpVtbl -> get_CanGoForward(This,canGoForward) ) + +#define ICoreWebView2_3_GoBack(This) \ + ( (This)->lpVtbl -> GoBack(This) ) + +#define ICoreWebView2_3_GoForward(This) \ + ( (This)->lpVtbl -> GoForward(This) ) + +#define ICoreWebView2_3_GetDevToolsProtocolEventReceiver(This,eventName,receiver) \ + ( (This)->lpVtbl -> GetDevToolsProtocolEventReceiver(This,eventName,receiver) ) + +#define ICoreWebView2_3_Stop(This) \ + ( (This)->lpVtbl -> Stop(This) ) + +#define ICoreWebView2_3_add_NewWindowRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NewWindowRequested(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_NewWindowRequested(This,token) \ + ( (This)->lpVtbl -> remove_NewWindowRequested(This,token) ) + +#define ICoreWebView2_3_add_DocumentTitleChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_DocumentTitleChanged(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_DocumentTitleChanged(This,token) \ + ( (This)->lpVtbl -> remove_DocumentTitleChanged(This,token) ) + +#define ICoreWebView2_3_get_DocumentTitle(This,title) \ + ( (This)->lpVtbl -> get_DocumentTitle(This,title) ) + +#define ICoreWebView2_3_AddHostObjectToScript(This,name,object) \ + ( (This)->lpVtbl -> AddHostObjectToScript(This,name,object) ) + +#define ICoreWebView2_3_RemoveHostObjectFromScript(This,name) \ + ( (This)->lpVtbl -> RemoveHostObjectFromScript(This,name) ) + +#define ICoreWebView2_3_OpenDevToolsWindow(This) \ + ( (This)->lpVtbl -> OpenDevToolsWindow(This) ) + +#define ICoreWebView2_3_add_ContainsFullScreenElementChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ContainsFullScreenElementChanged(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_ContainsFullScreenElementChanged(This,token) \ + ( (This)->lpVtbl -> remove_ContainsFullScreenElementChanged(This,token) ) + +#define ICoreWebView2_3_get_ContainsFullScreenElement(This,containsFullScreenElement) \ + ( (This)->lpVtbl -> get_ContainsFullScreenElement(This,containsFullScreenElement) ) + +#define ICoreWebView2_3_add_WebResourceRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WebResourceRequested(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_WebResourceRequested(This,token) \ + ( (This)->lpVtbl -> remove_WebResourceRequested(This,token) ) + +#define ICoreWebView2_3_AddWebResourceRequestedFilter(This,uri,resourceContext) \ + ( (This)->lpVtbl -> AddWebResourceRequestedFilter(This,uri,resourceContext) ) + +#define ICoreWebView2_3_RemoveWebResourceRequestedFilter(This,uri,resourceContext) \ + ( (This)->lpVtbl -> RemoveWebResourceRequestedFilter(This,uri,resourceContext) ) + +#define ICoreWebView2_3_add_WindowCloseRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WindowCloseRequested(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_WindowCloseRequested(This,token) \ + ( (This)->lpVtbl -> remove_WindowCloseRequested(This,token) ) + + +#define ICoreWebView2_3_add_WebResourceResponseReceived(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_WebResourceResponseReceived(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_WebResourceResponseReceived(This,token) \ + ( (This)->lpVtbl -> remove_WebResourceResponseReceived(This,token) ) + +#define ICoreWebView2_3_NavigateWithWebResourceRequest(This,request) \ + ( (This)->lpVtbl -> NavigateWithWebResourceRequest(This,request) ) + +#define ICoreWebView2_3_add_DOMContentLoaded(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_DOMContentLoaded(This,eventHandler,token) ) + +#define ICoreWebView2_3_remove_DOMContentLoaded(This,token) \ + ( (This)->lpVtbl -> remove_DOMContentLoaded(This,token) ) + +#define ICoreWebView2_3_get_CookieManager(This,cookieManager) \ + ( (This)->lpVtbl -> get_CookieManager(This,cookieManager) ) + +#define ICoreWebView2_3_get_Environment(This,environment) \ + ( (This)->lpVtbl -> get_Environment(This,environment) ) + + +#define ICoreWebView2_3_TrySuspend(This,handler) \ + ( (This)->lpVtbl -> TrySuspend(This,handler) ) + +#define ICoreWebView2_3_Resume(This) \ + ( (This)->lpVtbl -> Resume(This) ) + +#define ICoreWebView2_3_get_IsSuspended(This,isSuspended) \ + ( (This)->lpVtbl -> get_IsSuspended(This,isSuspended) ) + +#define ICoreWebView2_3_SetVirtualHostNameToFolderMapping(This,hostName,folderPath,accessKind) \ + ( (This)->lpVtbl -> SetVirtualHostNameToFolderMapping(This,hostName,folderPath,accessKind) ) + +#define ICoreWebView2_3_ClearVirtualHostNameToFolderMapping(This,hostName) \ + ( (This)->lpVtbl -> ClearVirtualHostNameToFolderMapping(This,hostName) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2_3_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CompositionController_INTERFACE_DEFINED__ +#define __ICoreWebView2CompositionController_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CompositionController */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CompositionController = {0x3df9b733,0xb9ae,0x4a15,{0x86,0xb4,0xeb,0x9e,0xe9,0x82,0x64,0x69}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("3df9b733-b9ae-4a15-86b4-eb9ee9826469") + ICoreWebView2CompositionController : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RootVisualTarget( + /* [retval][out] */ IUnknown **target) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_RootVisualTarget( + /* [in] */ IUnknown *target) = 0; + + virtual HRESULT STDMETHODCALLTYPE SendMouseInput( + /* [in] */ COREWEBVIEW2_MOUSE_EVENT_KIND eventKind, + /* [in] */ COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS virtualKeys, + /* [in] */ UINT32 mouseData, + /* [in] */ POINT point) = 0; + + virtual HRESULT STDMETHODCALLTYPE SendPointerInput( + /* [in] */ COREWEBVIEW2_POINTER_EVENT_KIND eventKind, + /* [in] */ ICoreWebView2PointerInfo *pointerInfo) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Cursor( + /* [retval][out] */ HCURSOR *cursor) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_SystemCursorId( + /* [retval][out] */ UINT32 *systemCursorId) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_CursorChanged( + /* [in] */ ICoreWebView2CursorChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_CursorChanged( + /* [in] */ EventRegistrationToken token) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CompositionControllerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CompositionController * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CompositionController * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CompositionController * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_RootVisualTarget )( + ICoreWebView2CompositionController * This, + /* [retval][out] */ IUnknown **target); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_RootVisualTarget )( + ICoreWebView2CompositionController * This, + /* [in] */ IUnknown *target); + + HRESULT ( STDMETHODCALLTYPE *SendMouseInput )( + ICoreWebView2CompositionController * This, + /* [in] */ COREWEBVIEW2_MOUSE_EVENT_KIND eventKind, + /* [in] */ COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS virtualKeys, + /* [in] */ UINT32 mouseData, + /* [in] */ POINT point); + + HRESULT ( STDMETHODCALLTYPE *SendPointerInput )( + ICoreWebView2CompositionController * This, + /* [in] */ COREWEBVIEW2_POINTER_EVENT_KIND eventKind, + /* [in] */ ICoreWebView2PointerInfo *pointerInfo); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Cursor )( + ICoreWebView2CompositionController * This, + /* [retval][out] */ HCURSOR *cursor); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_SystemCursorId )( + ICoreWebView2CompositionController * This, + /* [retval][out] */ UINT32 *systemCursorId); + + HRESULT ( STDMETHODCALLTYPE *add_CursorChanged )( + ICoreWebView2CompositionController * This, + /* [in] */ ICoreWebView2CursorChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_CursorChanged )( + ICoreWebView2CompositionController * This, + /* [in] */ EventRegistrationToken token); + + END_INTERFACE + } ICoreWebView2CompositionControllerVtbl; + + interface ICoreWebView2CompositionController + { + CONST_VTBL struct ICoreWebView2CompositionControllerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CompositionController_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CompositionController_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CompositionController_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CompositionController_get_RootVisualTarget(This,target) \ + ( (This)->lpVtbl -> get_RootVisualTarget(This,target) ) + +#define ICoreWebView2CompositionController_put_RootVisualTarget(This,target) \ + ( (This)->lpVtbl -> put_RootVisualTarget(This,target) ) + +#define ICoreWebView2CompositionController_SendMouseInput(This,eventKind,virtualKeys,mouseData,point) \ + ( (This)->lpVtbl -> SendMouseInput(This,eventKind,virtualKeys,mouseData,point) ) + +#define ICoreWebView2CompositionController_SendPointerInput(This,eventKind,pointerInfo) \ + ( (This)->lpVtbl -> SendPointerInput(This,eventKind,pointerInfo) ) + +#define ICoreWebView2CompositionController_get_Cursor(This,cursor) \ + ( (This)->lpVtbl -> get_Cursor(This,cursor) ) + +#define ICoreWebView2CompositionController_get_SystemCursorId(This,systemCursorId) \ + ( (This)->lpVtbl -> get_SystemCursorId(This,systemCursorId) ) + +#define ICoreWebView2CompositionController_add_CursorChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_CursorChanged(This,eventHandler,token) ) + +#define ICoreWebView2CompositionController_remove_CursorChanged(This,token) \ + ( (This)->lpVtbl -> remove_CursorChanged(This,token) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CompositionController_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CompositionController2_INTERFACE_DEFINED__ +#define __ICoreWebView2CompositionController2_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CompositionController2 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CompositionController2 = {0x0b6a3d24,0x49cb,0x4806,{0xba,0x20,0xb5,0xe0,0x73,0x4a,0x7b,0x26}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("0b6a3d24-49cb-4806-ba20-b5e0734a7b26") + ICoreWebView2CompositionController2 : public ICoreWebView2CompositionController + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_UIAProvider( + /* [retval][out] */ IUnknown **provider) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CompositionController2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CompositionController2 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CompositionController2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CompositionController2 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_RootVisualTarget )( + ICoreWebView2CompositionController2 * This, + /* [retval][out] */ IUnknown **target); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_RootVisualTarget )( + ICoreWebView2CompositionController2 * This, + /* [in] */ IUnknown *target); + + HRESULT ( STDMETHODCALLTYPE *SendMouseInput )( + ICoreWebView2CompositionController2 * This, + /* [in] */ COREWEBVIEW2_MOUSE_EVENT_KIND eventKind, + /* [in] */ COREWEBVIEW2_MOUSE_EVENT_VIRTUAL_KEYS virtualKeys, + /* [in] */ UINT32 mouseData, + /* [in] */ POINT point); + + HRESULT ( STDMETHODCALLTYPE *SendPointerInput )( + ICoreWebView2CompositionController2 * This, + /* [in] */ COREWEBVIEW2_POINTER_EVENT_KIND eventKind, + /* [in] */ ICoreWebView2PointerInfo *pointerInfo); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Cursor )( + ICoreWebView2CompositionController2 * This, + /* [retval][out] */ HCURSOR *cursor); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_SystemCursorId )( + ICoreWebView2CompositionController2 * This, + /* [retval][out] */ UINT32 *systemCursorId); + + HRESULT ( STDMETHODCALLTYPE *add_CursorChanged )( + ICoreWebView2CompositionController2 * This, + /* [in] */ ICoreWebView2CursorChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_CursorChanged )( + ICoreWebView2CompositionController2 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_UIAProvider )( + ICoreWebView2CompositionController2 * This, + /* [retval][out] */ IUnknown **provider); + + END_INTERFACE + } ICoreWebView2CompositionController2Vtbl; + + interface ICoreWebView2CompositionController2 + { + CONST_VTBL struct ICoreWebView2CompositionController2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CompositionController2_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CompositionController2_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CompositionController2_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CompositionController2_get_RootVisualTarget(This,target) \ + ( (This)->lpVtbl -> get_RootVisualTarget(This,target) ) + +#define ICoreWebView2CompositionController2_put_RootVisualTarget(This,target) \ + ( (This)->lpVtbl -> put_RootVisualTarget(This,target) ) + +#define ICoreWebView2CompositionController2_SendMouseInput(This,eventKind,virtualKeys,mouseData,point) \ + ( (This)->lpVtbl -> SendMouseInput(This,eventKind,virtualKeys,mouseData,point) ) + +#define ICoreWebView2CompositionController2_SendPointerInput(This,eventKind,pointerInfo) \ + ( (This)->lpVtbl -> SendPointerInput(This,eventKind,pointerInfo) ) + +#define ICoreWebView2CompositionController2_get_Cursor(This,cursor) \ + ( (This)->lpVtbl -> get_Cursor(This,cursor) ) + +#define ICoreWebView2CompositionController2_get_SystemCursorId(This,systemCursorId) \ + ( (This)->lpVtbl -> get_SystemCursorId(This,systemCursorId) ) + +#define ICoreWebView2CompositionController2_add_CursorChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_CursorChanged(This,eventHandler,token) ) + +#define ICoreWebView2CompositionController2_remove_CursorChanged(This,token) \ + ( (This)->lpVtbl -> remove_CursorChanged(This,token) ) + + +#define ICoreWebView2CompositionController2_get_UIAProvider(This,provider) \ + ( (This)->lpVtbl -> get_UIAProvider(This,provider) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CompositionController2_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Controller_INTERFACE_DEFINED__ +#define __ICoreWebView2Controller_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Controller */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Controller = {0x4d00c0d1,0x9434,0x4eb6,{0x80,0x78,0x86,0x97,0xa5,0x60,0x33,0x4f}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("4d00c0d1-9434-4eb6-8078-8697a560334f") + ICoreWebView2Controller : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsVisible( + /* [retval][out] */ BOOL *isVisible) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsVisible( + /* [in] */ BOOL isVisible) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Bounds( + /* [retval][out] */ RECT *bounds) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Bounds( + /* [in] */ RECT bounds) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ZoomFactor( + /* [retval][out] */ double *zoomFactor) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_ZoomFactor( + /* [in] */ double zoomFactor) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_ZoomFactorChanged( + /* [in] */ ICoreWebView2ZoomFactorChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_ZoomFactorChanged( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetBoundsAndZoomFactor( + /* [in] */ RECT bounds, + /* [in] */ double zoomFactor) = 0; + + virtual HRESULT STDMETHODCALLTYPE MoveFocus( + /* [in] */ COREWEBVIEW2_MOVE_FOCUS_REASON reason) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_MoveFocusRequested( + /* [in] */ ICoreWebView2MoveFocusRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_MoveFocusRequested( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_GotFocus( + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_GotFocus( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_LostFocus( + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_LostFocus( + /* [in] */ EventRegistrationToken token) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_AcceleratorKeyPressed( + /* [in] */ ICoreWebView2AcceleratorKeyPressedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_AcceleratorKeyPressed( + /* [in] */ EventRegistrationToken token) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ParentWindow( + /* [retval][out] */ HWND *parentWindow) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_ParentWindow( + /* [in] */ HWND parentWindow) = 0; + + virtual HRESULT STDMETHODCALLTYPE NotifyParentWindowPositionChanged( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( void) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CoreWebView2( + /* [retval][out] */ ICoreWebView2 **coreWebView2) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ControllerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Controller * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Controller * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Controller * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsVisible )( + ICoreWebView2Controller * This, + /* [retval][out] */ BOOL *isVisible); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsVisible )( + ICoreWebView2Controller * This, + /* [in] */ BOOL isVisible); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Bounds )( + ICoreWebView2Controller * This, + /* [retval][out] */ RECT *bounds); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Bounds )( + ICoreWebView2Controller * This, + /* [in] */ RECT bounds); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ZoomFactor )( + ICoreWebView2Controller * This, + /* [retval][out] */ double *zoomFactor); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ZoomFactor )( + ICoreWebView2Controller * This, + /* [in] */ double zoomFactor); + + HRESULT ( STDMETHODCALLTYPE *add_ZoomFactorChanged )( + ICoreWebView2Controller * This, + /* [in] */ ICoreWebView2ZoomFactorChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ZoomFactorChanged )( + ICoreWebView2Controller * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *SetBoundsAndZoomFactor )( + ICoreWebView2Controller * This, + /* [in] */ RECT bounds, + /* [in] */ double zoomFactor); + + HRESULT ( STDMETHODCALLTYPE *MoveFocus )( + ICoreWebView2Controller * This, + /* [in] */ COREWEBVIEW2_MOVE_FOCUS_REASON reason); + + HRESULT ( STDMETHODCALLTYPE *add_MoveFocusRequested )( + ICoreWebView2Controller * This, + /* [in] */ ICoreWebView2MoveFocusRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_MoveFocusRequested )( + ICoreWebView2Controller * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_GotFocus )( + ICoreWebView2Controller * This, + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_GotFocus )( + ICoreWebView2Controller * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_LostFocus )( + ICoreWebView2Controller * This, + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_LostFocus )( + ICoreWebView2Controller * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_AcceleratorKeyPressed )( + ICoreWebView2Controller * This, + /* [in] */ ICoreWebView2AcceleratorKeyPressedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_AcceleratorKeyPressed )( + ICoreWebView2Controller * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ParentWindow )( + ICoreWebView2Controller * This, + /* [retval][out] */ HWND *parentWindow); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ParentWindow )( + ICoreWebView2Controller * This, + /* [in] */ HWND parentWindow); + + HRESULT ( STDMETHODCALLTYPE *NotifyParentWindowPositionChanged )( + ICoreWebView2Controller * This); + + HRESULT ( STDMETHODCALLTYPE *Close )( + ICoreWebView2Controller * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CoreWebView2 )( + ICoreWebView2Controller * This, + /* [retval][out] */ ICoreWebView2 **coreWebView2); + + END_INTERFACE + } ICoreWebView2ControllerVtbl; + + interface ICoreWebView2Controller + { + CONST_VTBL struct ICoreWebView2ControllerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Controller_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Controller_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Controller_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Controller_get_IsVisible(This,isVisible) \ + ( (This)->lpVtbl -> get_IsVisible(This,isVisible) ) + +#define ICoreWebView2Controller_put_IsVisible(This,isVisible) \ + ( (This)->lpVtbl -> put_IsVisible(This,isVisible) ) + +#define ICoreWebView2Controller_get_Bounds(This,bounds) \ + ( (This)->lpVtbl -> get_Bounds(This,bounds) ) + +#define ICoreWebView2Controller_put_Bounds(This,bounds) \ + ( (This)->lpVtbl -> put_Bounds(This,bounds) ) + +#define ICoreWebView2Controller_get_ZoomFactor(This,zoomFactor) \ + ( (This)->lpVtbl -> get_ZoomFactor(This,zoomFactor) ) + +#define ICoreWebView2Controller_put_ZoomFactor(This,zoomFactor) \ + ( (This)->lpVtbl -> put_ZoomFactor(This,zoomFactor) ) + +#define ICoreWebView2Controller_add_ZoomFactorChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ZoomFactorChanged(This,eventHandler,token) ) + +#define ICoreWebView2Controller_remove_ZoomFactorChanged(This,token) \ + ( (This)->lpVtbl -> remove_ZoomFactorChanged(This,token) ) + +#define ICoreWebView2Controller_SetBoundsAndZoomFactor(This,bounds,zoomFactor) \ + ( (This)->lpVtbl -> SetBoundsAndZoomFactor(This,bounds,zoomFactor) ) + +#define ICoreWebView2Controller_MoveFocus(This,reason) \ + ( (This)->lpVtbl -> MoveFocus(This,reason) ) + +#define ICoreWebView2Controller_add_MoveFocusRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_MoveFocusRequested(This,eventHandler,token) ) + +#define ICoreWebView2Controller_remove_MoveFocusRequested(This,token) \ + ( (This)->lpVtbl -> remove_MoveFocusRequested(This,token) ) + +#define ICoreWebView2Controller_add_GotFocus(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_GotFocus(This,eventHandler,token) ) + +#define ICoreWebView2Controller_remove_GotFocus(This,token) \ + ( (This)->lpVtbl -> remove_GotFocus(This,token) ) + +#define ICoreWebView2Controller_add_LostFocus(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_LostFocus(This,eventHandler,token) ) + +#define ICoreWebView2Controller_remove_LostFocus(This,token) \ + ( (This)->lpVtbl -> remove_LostFocus(This,token) ) + +#define ICoreWebView2Controller_add_AcceleratorKeyPressed(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_AcceleratorKeyPressed(This,eventHandler,token) ) + +#define ICoreWebView2Controller_remove_AcceleratorKeyPressed(This,token) \ + ( (This)->lpVtbl -> remove_AcceleratorKeyPressed(This,token) ) + +#define ICoreWebView2Controller_get_ParentWindow(This,parentWindow) \ + ( (This)->lpVtbl -> get_ParentWindow(This,parentWindow) ) + +#define ICoreWebView2Controller_put_ParentWindow(This,parentWindow) \ + ( (This)->lpVtbl -> put_ParentWindow(This,parentWindow) ) + +#define ICoreWebView2Controller_NotifyParentWindowPositionChanged(This) \ + ( (This)->lpVtbl -> NotifyParentWindowPositionChanged(This) ) + +#define ICoreWebView2Controller_Close(This) \ + ( (This)->lpVtbl -> Close(This) ) + +#define ICoreWebView2Controller_get_CoreWebView2(This,coreWebView2) \ + ( (This)->lpVtbl -> get_CoreWebView2(This,coreWebView2) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Controller_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Controller2_INTERFACE_DEFINED__ +#define __ICoreWebView2Controller2_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Controller2 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Controller2 = {0xc979903e,0xd4ca,0x4228,{0x92,0xeb,0x47,0xee,0x3f,0xa9,0x6e,0xab}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("c979903e-d4ca-4228-92eb-47ee3fa96eab") + ICoreWebView2Controller2 : public ICoreWebView2Controller + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_DefaultBackgroundColor( + /* [retval][out] */ COREWEBVIEW2_COLOR *backgroundColor) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_DefaultBackgroundColor( + /* [in] */ COREWEBVIEW2_COLOR backgroundColor) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Controller2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Controller2 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Controller2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Controller2 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsVisible )( + ICoreWebView2Controller2 * This, + /* [retval][out] */ BOOL *isVisible); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsVisible )( + ICoreWebView2Controller2 * This, + /* [in] */ BOOL isVisible); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Bounds )( + ICoreWebView2Controller2 * This, + /* [retval][out] */ RECT *bounds); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Bounds )( + ICoreWebView2Controller2 * This, + /* [in] */ RECT bounds); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ZoomFactor )( + ICoreWebView2Controller2 * This, + /* [retval][out] */ double *zoomFactor); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ZoomFactor )( + ICoreWebView2Controller2 * This, + /* [in] */ double zoomFactor); + + HRESULT ( STDMETHODCALLTYPE *add_ZoomFactorChanged )( + ICoreWebView2Controller2 * This, + /* [in] */ ICoreWebView2ZoomFactorChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ZoomFactorChanged )( + ICoreWebView2Controller2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *SetBoundsAndZoomFactor )( + ICoreWebView2Controller2 * This, + /* [in] */ RECT bounds, + /* [in] */ double zoomFactor); + + HRESULT ( STDMETHODCALLTYPE *MoveFocus )( + ICoreWebView2Controller2 * This, + /* [in] */ COREWEBVIEW2_MOVE_FOCUS_REASON reason); + + HRESULT ( STDMETHODCALLTYPE *add_MoveFocusRequested )( + ICoreWebView2Controller2 * This, + /* [in] */ ICoreWebView2MoveFocusRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_MoveFocusRequested )( + ICoreWebView2Controller2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_GotFocus )( + ICoreWebView2Controller2 * This, + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_GotFocus )( + ICoreWebView2Controller2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_LostFocus )( + ICoreWebView2Controller2 * This, + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_LostFocus )( + ICoreWebView2Controller2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_AcceleratorKeyPressed )( + ICoreWebView2Controller2 * This, + /* [in] */ ICoreWebView2AcceleratorKeyPressedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_AcceleratorKeyPressed )( + ICoreWebView2Controller2 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ParentWindow )( + ICoreWebView2Controller2 * This, + /* [retval][out] */ HWND *parentWindow); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ParentWindow )( + ICoreWebView2Controller2 * This, + /* [in] */ HWND parentWindow); + + HRESULT ( STDMETHODCALLTYPE *NotifyParentWindowPositionChanged )( + ICoreWebView2Controller2 * This); + + HRESULT ( STDMETHODCALLTYPE *Close )( + ICoreWebView2Controller2 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CoreWebView2 )( + ICoreWebView2Controller2 * This, + /* [retval][out] */ ICoreWebView2 **coreWebView2); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DefaultBackgroundColor )( + ICoreWebView2Controller2 * This, + /* [retval][out] */ COREWEBVIEW2_COLOR *backgroundColor); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_DefaultBackgroundColor )( + ICoreWebView2Controller2 * This, + /* [in] */ COREWEBVIEW2_COLOR backgroundColor); + + END_INTERFACE + } ICoreWebView2Controller2Vtbl; + + interface ICoreWebView2Controller2 + { + CONST_VTBL struct ICoreWebView2Controller2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Controller2_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Controller2_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Controller2_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Controller2_get_IsVisible(This,isVisible) \ + ( (This)->lpVtbl -> get_IsVisible(This,isVisible) ) + +#define ICoreWebView2Controller2_put_IsVisible(This,isVisible) \ + ( (This)->lpVtbl -> put_IsVisible(This,isVisible) ) + +#define ICoreWebView2Controller2_get_Bounds(This,bounds) \ + ( (This)->lpVtbl -> get_Bounds(This,bounds) ) + +#define ICoreWebView2Controller2_put_Bounds(This,bounds) \ + ( (This)->lpVtbl -> put_Bounds(This,bounds) ) + +#define ICoreWebView2Controller2_get_ZoomFactor(This,zoomFactor) \ + ( (This)->lpVtbl -> get_ZoomFactor(This,zoomFactor) ) + +#define ICoreWebView2Controller2_put_ZoomFactor(This,zoomFactor) \ + ( (This)->lpVtbl -> put_ZoomFactor(This,zoomFactor) ) + +#define ICoreWebView2Controller2_add_ZoomFactorChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ZoomFactorChanged(This,eventHandler,token) ) + +#define ICoreWebView2Controller2_remove_ZoomFactorChanged(This,token) \ + ( (This)->lpVtbl -> remove_ZoomFactorChanged(This,token) ) + +#define ICoreWebView2Controller2_SetBoundsAndZoomFactor(This,bounds,zoomFactor) \ + ( (This)->lpVtbl -> SetBoundsAndZoomFactor(This,bounds,zoomFactor) ) + +#define ICoreWebView2Controller2_MoveFocus(This,reason) \ + ( (This)->lpVtbl -> MoveFocus(This,reason) ) + +#define ICoreWebView2Controller2_add_MoveFocusRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_MoveFocusRequested(This,eventHandler,token) ) + +#define ICoreWebView2Controller2_remove_MoveFocusRequested(This,token) \ + ( (This)->lpVtbl -> remove_MoveFocusRequested(This,token) ) + +#define ICoreWebView2Controller2_add_GotFocus(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_GotFocus(This,eventHandler,token) ) + +#define ICoreWebView2Controller2_remove_GotFocus(This,token) \ + ( (This)->lpVtbl -> remove_GotFocus(This,token) ) + +#define ICoreWebView2Controller2_add_LostFocus(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_LostFocus(This,eventHandler,token) ) + +#define ICoreWebView2Controller2_remove_LostFocus(This,token) \ + ( (This)->lpVtbl -> remove_LostFocus(This,token) ) + +#define ICoreWebView2Controller2_add_AcceleratorKeyPressed(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_AcceleratorKeyPressed(This,eventHandler,token) ) + +#define ICoreWebView2Controller2_remove_AcceleratorKeyPressed(This,token) \ + ( (This)->lpVtbl -> remove_AcceleratorKeyPressed(This,token) ) + +#define ICoreWebView2Controller2_get_ParentWindow(This,parentWindow) \ + ( (This)->lpVtbl -> get_ParentWindow(This,parentWindow) ) + +#define ICoreWebView2Controller2_put_ParentWindow(This,parentWindow) \ + ( (This)->lpVtbl -> put_ParentWindow(This,parentWindow) ) + +#define ICoreWebView2Controller2_NotifyParentWindowPositionChanged(This) \ + ( (This)->lpVtbl -> NotifyParentWindowPositionChanged(This) ) + +#define ICoreWebView2Controller2_Close(This) \ + ( (This)->lpVtbl -> Close(This) ) + +#define ICoreWebView2Controller2_get_CoreWebView2(This,coreWebView2) \ + ( (This)->lpVtbl -> get_CoreWebView2(This,coreWebView2) ) + + +#define ICoreWebView2Controller2_get_DefaultBackgroundColor(This,backgroundColor) \ + ( (This)->lpVtbl -> get_DefaultBackgroundColor(This,backgroundColor) ) + +#define ICoreWebView2Controller2_put_DefaultBackgroundColor(This,backgroundColor) \ + ( (This)->lpVtbl -> put_DefaultBackgroundColor(This,backgroundColor) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Controller2_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Controller3_INTERFACE_DEFINED__ +#define __ICoreWebView2Controller3_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Controller3 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Controller3 = {0xf9614724,0x5d2b,0x41dc,{0xae,0xf7,0x73,0xd6,0x2b,0x51,0x54,0x3b}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("f9614724-5d2b-41dc-aef7-73d62b51543b") + ICoreWebView2Controller3 : public ICoreWebView2Controller2 + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RasterizationScale( + /* [retval][out] */ double *scale) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_RasterizationScale( + /* [in] */ double scale) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ShouldDetectMonitorScaleChanges( + /* [retval][out] */ BOOL *value) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_ShouldDetectMonitorScaleChanges( + /* [in] */ BOOL value) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_RasterizationScaleChanged( + /* [in] */ ICoreWebView2RasterizationScaleChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_RasterizationScaleChanged( + /* [in] */ EventRegistrationToken token) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_BoundsMode( + /* [retval][out] */ COREWEBVIEW2_BOUNDS_MODE *boundsMode) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_BoundsMode( + /* [in] */ COREWEBVIEW2_BOUNDS_MODE boundsMode) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Controller3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Controller3 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Controller3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Controller3 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsVisible )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ BOOL *isVisible); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsVisible )( + ICoreWebView2Controller3 * This, + /* [in] */ BOOL isVisible); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Bounds )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ RECT *bounds); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Bounds )( + ICoreWebView2Controller3 * This, + /* [in] */ RECT bounds); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ZoomFactor )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ double *zoomFactor); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ZoomFactor )( + ICoreWebView2Controller3 * This, + /* [in] */ double zoomFactor); + + HRESULT ( STDMETHODCALLTYPE *add_ZoomFactorChanged )( + ICoreWebView2Controller3 * This, + /* [in] */ ICoreWebView2ZoomFactorChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_ZoomFactorChanged )( + ICoreWebView2Controller3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *SetBoundsAndZoomFactor )( + ICoreWebView2Controller3 * This, + /* [in] */ RECT bounds, + /* [in] */ double zoomFactor); + + HRESULT ( STDMETHODCALLTYPE *MoveFocus )( + ICoreWebView2Controller3 * This, + /* [in] */ COREWEBVIEW2_MOVE_FOCUS_REASON reason); + + HRESULT ( STDMETHODCALLTYPE *add_MoveFocusRequested )( + ICoreWebView2Controller3 * This, + /* [in] */ ICoreWebView2MoveFocusRequestedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_MoveFocusRequested )( + ICoreWebView2Controller3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_GotFocus )( + ICoreWebView2Controller3 * This, + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_GotFocus )( + ICoreWebView2Controller3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_LostFocus )( + ICoreWebView2Controller3 * This, + /* [in] */ ICoreWebView2FocusChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_LostFocus )( + ICoreWebView2Controller3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *add_AcceleratorKeyPressed )( + ICoreWebView2Controller3 * This, + /* [in] */ ICoreWebView2AcceleratorKeyPressedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_AcceleratorKeyPressed )( + ICoreWebView2Controller3 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ParentWindow )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ HWND *parentWindow); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ParentWindow )( + ICoreWebView2Controller3 * This, + /* [in] */ HWND parentWindow); + + HRESULT ( STDMETHODCALLTYPE *NotifyParentWindowPositionChanged )( + ICoreWebView2Controller3 * This); + + HRESULT ( STDMETHODCALLTYPE *Close )( + ICoreWebView2Controller3 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_CoreWebView2 )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ ICoreWebView2 **coreWebView2); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DefaultBackgroundColor )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ COREWEBVIEW2_COLOR *backgroundColor); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_DefaultBackgroundColor )( + ICoreWebView2Controller3 * This, + /* [in] */ COREWEBVIEW2_COLOR backgroundColor); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_RasterizationScale )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ double *scale); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_RasterizationScale )( + ICoreWebView2Controller3 * This, + /* [in] */ double scale); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ShouldDetectMonitorScaleChanges )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ BOOL *value); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ShouldDetectMonitorScaleChanges )( + ICoreWebView2Controller3 * This, + /* [in] */ BOOL value); + + HRESULT ( STDMETHODCALLTYPE *add_RasterizationScaleChanged )( + ICoreWebView2Controller3 * This, + /* [in] */ ICoreWebView2RasterizationScaleChangedEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_RasterizationScaleChanged )( + ICoreWebView2Controller3 * This, + /* [in] */ EventRegistrationToken token); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BoundsMode )( + ICoreWebView2Controller3 * This, + /* [retval][out] */ COREWEBVIEW2_BOUNDS_MODE *boundsMode); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_BoundsMode )( + ICoreWebView2Controller3 * This, + /* [in] */ COREWEBVIEW2_BOUNDS_MODE boundsMode); + + END_INTERFACE + } ICoreWebView2Controller3Vtbl; + + interface ICoreWebView2Controller3 + { + CONST_VTBL struct ICoreWebView2Controller3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Controller3_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Controller3_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Controller3_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Controller3_get_IsVisible(This,isVisible) \ + ( (This)->lpVtbl -> get_IsVisible(This,isVisible) ) + +#define ICoreWebView2Controller3_put_IsVisible(This,isVisible) \ + ( (This)->lpVtbl -> put_IsVisible(This,isVisible) ) + +#define ICoreWebView2Controller3_get_Bounds(This,bounds) \ + ( (This)->lpVtbl -> get_Bounds(This,bounds) ) + +#define ICoreWebView2Controller3_put_Bounds(This,bounds) \ + ( (This)->lpVtbl -> put_Bounds(This,bounds) ) + +#define ICoreWebView2Controller3_get_ZoomFactor(This,zoomFactor) \ + ( (This)->lpVtbl -> get_ZoomFactor(This,zoomFactor) ) + +#define ICoreWebView2Controller3_put_ZoomFactor(This,zoomFactor) \ + ( (This)->lpVtbl -> put_ZoomFactor(This,zoomFactor) ) + +#define ICoreWebView2Controller3_add_ZoomFactorChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_ZoomFactorChanged(This,eventHandler,token) ) + +#define ICoreWebView2Controller3_remove_ZoomFactorChanged(This,token) \ + ( (This)->lpVtbl -> remove_ZoomFactorChanged(This,token) ) + +#define ICoreWebView2Controller3_SetBoundsAndZoomFactor(This,bounds,zoomFactor) \ + ( (This)->lpVtbl -> SetBoundsAndZoomFactor(This,bounds,zoomFactor) ) + +#define ICoreWebView2Controller3_MoveFocus(This,reason) \ + ( (This)->lpVtbl -> MoveFocus(This,reason) ) + +#define ICoreWebView2Controller3_add_MoveFocusRequested(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_MoveFocusRequested(This,eventHandler,token) ) + +#define ICoreWebView2Controller3_remove_MoveFocusRequested(This,token) \ + ( (This)->lpVtbl -> remove_MoveFocusRequested(This,token) ) + +#define ICoreWebView2Controller3_add_GotFocus(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_GotFocus(This,eventHandler,token) ) + +#define ICoreWebView2Controller3_remove_GotFocus(This,token) \ + ( (This)->lpVtbl -> remove_GotFocus(This,token) ) + +#define ICoreWebView2Controller3_add_LostFocus(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_LostFocus(This,eventHandler,token) ) + +#define ICoreWebView2Controller3_remove_LostFocus(This,token) \ + ( (This)->lpVtbl -> remove_LostFocus(This,token) ) + +#define ICoreWebView2Controller3_add_AcceleratorKeyPressed(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_AcceleratorKeyPressed(This,eventHandler,token) ) + +#define ICoreWebView2Controller3_remove_AcceleratorKeyPressed(This,token) \ + ( (This)->lpVtbl -> remove_AcceleratorKeyPressed(This,token) ) + +#define ICoreWebView2Controller3_get_ParentWindow(This,parentWindow) \ + ( (This)->lpVtbl -> get_ParentWindow(This,parentWindow) ) + +#define ICoreWebView2Controller3_put_ParentWindow(This,parentWindow) \ + ( (This)->lpVtbl -> put_ParentWindow(This,parentWindow) ) + +#define ICoreWebView2Controller3_NotifyParentWindowPositionChanged(This) \ + ( (This)->lpVtbl -> NotifyParentWindowPositionChanged(This) ) + +#define ICoreWebView2Controller3_Close(This) \ + ( (This)->lpVtbl -> Close(This) ) + +#define ICoreWebView2Controller3_get_CoreWebView2(This,coreWebView2) \ + ( (This)->lpVtbl -> get_CoreWebView2(This,coreWebView2) ) + + +#define ICoreWebView2Controller3_get_DefaultBackgroundColor(This,backgroundColor) \ + ( (This)->lpVtbl -> get_DefaultBackgroundColor(This,backgroundColor) ) + +#define ICoreWebView2Controller3_put_DefaultBackgroundColor(This,backgroundColor) \ + ( (This)->lpVtbl -> put_DefaultBackgroundColor(This,backgroundColor) ) + + +#define ICoreWebView2Controller3_get_RasterizationScale(This,scale) \ + ( (This)->lpVtbl -> get_RasterizationScale(This,scale) ) + +#define ICoreWebView2Controller3_put_RasterizationScale(This,scale) \ + ( (This)->lpVtbl -> put_RasterizationScale(This,scale) ) + +#define ICoreWebView2Controller3_get_ShouldDetectMonitorScaleChanges(This,value) \ + ( (This)->lpVtbl -> get_ShouldDetectMonitorScaleChanges(This,value) ) + +#define ICoreWebView2Controller3_put_ShouldDetectMonitorScaleChanges(This,value) \ + ( (This)->lpVtbl -> put_ShouldDetectMonitorScaleChanges(This,value) ) + +#define ICoreWebView2Controller3_add_RasterizationScaleChanged(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_RasterizationScaleChanged(This,eventHandler,token) ) + +#define ICoreWebView2Controller3_remove_RasterizationScaleChanged(This,token) \ + ( (This)->lpVtbl -> remove_RasterizationScaleChanged(This,token) ) + +#define ICoreWebView2Controller3_get_BoundsMode(This,boundsMode) \ + ( (This)->lpVtbl -> get_BoundsMode(This,boundsMode) ) + +#define ICoreWebView2Controller3_put_BoundsMode(This,boundsMode) \ + ( (This)->lpVtbl -> put_BoundsMode(This,boundsMode) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Controller3_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ContentLoadingEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2ContentLoadingEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ContentLoadingEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ContentLoadingEventArgs = {0x0c8a1275,0x9b6b,0x4901,{0x87,0xad,0x70,0xdf,0x25,0xba,0xfa,0x6e}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("0c8a1275-9b6b-4901-87ad-70df25bafa6e") + ICoreWebView2ContentLoadingEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsErrorPage( + /* [retval][out] */ BOOL *isErrorPage) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_NavigationId( + /* [retval][out] */ UINT64 *navigationId) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ContentLoadingEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ContentLoadingEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ContentLoadingEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ContentLoadingEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsErrorPage )( + ICoreWebView2ContentLoadingEventArgs * This, + /* [retval][out] */ BOOL *isErrorPage); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_NavigationId )( + ICoreWebView2ContentLoadingEventArgs * This, + /* [retval][out] */ UINT64 *navigationId); + + END_INTERFACE + } ICoreWebView2ContentLoadingEventArgsVtbl; + + interface ICoreWebView2ContentLoadingEventArgs + { + CONST_VTBL struct ICoreWebView2ContentLoadingEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ContentLoadingEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ContentLoadingEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ContentLoadingEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ContentLoadingEventArgs_get_IsErrorPage(This,isErrorPage) \ + ( (This)->lpVtbl -> get_IsErrorPage(This,isErrorPage) ) + +#define ICoreWebView2ContentLoadingEventArgs_get_NavigationId(This,navigationId) \ + ( (This)->lpVtbl -> get_NavigationId(This,navigationId) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ContentLoadingEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ContentLoadingEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2ContentLoadingEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ContentLoadingEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ContentLoadingEventHandler = {0x364471e7,0xf2be,0x4910,{0xbd,0xba,0xd7,0x20,0x77,0xd5,0x1c,0x4b}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("364471e7-f2be-4910-bdba-d72077d51c4b") + ICoreWebView2ContentLoadingEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2ContentLoadingEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ContentLoadingEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ContentLoadingEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ContentLoadingEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ContentLoadingEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2ContentLoadingEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2ContentLoadingEventArgs *args); + + END_INTERFACE + } ICoreWebView2ContentLoadingEventHandlerVtbl; + + interface ICoreWebView2ContentLoadingEventHandler + { + CONST_VTBL struct ICoreWebView2ContentLoadingEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ContentLoadingEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ContentLoadingEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ContentLoadingEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ContentLoadingEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ContentLoadingEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Cookie_INTERFACE_DEFINED__ +#define __ICoreWebView2Cookie_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Cookie */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Cookie = {0xAD26D6BE,0x1486,0x43E6,{0xBF,0x87,0xA2,0x03,0x40,0x06,0xCA,0x21}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("AD26D6BE-1486-43E6-BF87-A2034006CA21") + ICoreWebView2Cookie : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Name( + /* [retval][out] */ LPWSTR *name) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Value( + /* [retval][out] */ LPWSTR *value) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Value( + /* [in] */ LPCWSTR value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Domain( + /* [retval][out] */ LPWSTR *domain) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Path( + /* [retval][out] */ LPWSTR *path) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Expires( + /* [retval][out] */ double *expires) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Expires( + /* [in] */ double expires) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsHttpOnly( + /* [retval][out] */ BOOL *isHttpOnly) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsHttpOnly( + /* [in] */ BOOL isHttpOnly) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_SameSite( + /* [retval][out] */ COREWEBVIEW2_COOKIE_SAME_SITE_KIND *sameSite) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_SameSite( + /* [in] */ COREWEBVIEW2_COOKIE_SAME_SITE_KIND sameSite) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsSecure( + /* [retval][out] */ BOOL *isSecure) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsSecure( + /* [in] */ BOOL isSecure) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsSession( + /* [retval][out] */ BOOL *isSession) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CookieVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Cookie * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Cookie * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Cookie * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Name )( + ICoreWebView2Cookie * This, + /* [retval][out] */ LPWSTR *name); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Value )( + ICoreWebView2Cookie * This, + /* [retval][out] */ LPWSTR *value); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Value )( + ICoreWebView2Cookie * This, + /* [in] */ LPCWSTR value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Domain )( + ICoreWebView2Cookie * This, + /* [retval][out] */ LPWSTR *domain); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Path )( + ICoreWebView2Cookie * This, + /* [retval][out] */ LPWSTR *path); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Expires )( + ICoreWebView2Cookie * This, + /* [retval][out] */ double *expires); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Expires )( + ICoreWebView2Cookie * This, + /* [in] */ double expires); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsHttpOnly )( + ICoreWebView2Cookie * This, + /* [retval][out] */ BOOL *isHttpOnly); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsHttpOnly )( + ICoreWebView2Cookie * This, + /* [in] */ BOOL isHttpOnly); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_SameSite )( + ICoreWebView2Cookie * This, + /* [retval][out] */ COREWEBVIEW2_COOKIE_SAME_SITE_KIND *sameSite); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_SameSite )( + ICoreWebView2Cookie * This, + /* [in] */ COREWEBVIEW2_COOKIE_SAME_SITE_KIND sameSite); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsSecure )( + ICoreWebView2Cookie * This, + /* [retval][out] */ BOOL *isSecure); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsSecure )( + ICoreWebView2Cookie * This, + /* [in] */ BOOL isSecure); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsSession )( + ICoreWebView2Cookie * This, + /* [retval][out] */ BOOL *isSession); + + END_INTERFACE + } ICoreWebView2CookieVtbl; + + interface ICoreWebView2Cookie + { + CONST_VTBL struct ICoreWebView2CookieVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Cookie_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Cookie_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Cookie_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Cookie_get_Name(This,name) \ + ( (This)->lpVtbl -> get_Name(This,name) ) + +#define ICoreWebView2Cookie_get_Value(This,value) \ + ( (This)->lpVtbl -> get_Value(This,value) ) + +#define ICoreWebView2Cookie_put_Value(This,value) \ + ( (This)->lpVtbl -> put_Value(This,value) ) + +#define ICoreWebView2Cookie_get_Domain(This,domain) \ + ( (This)->lpVtbl -> get_Domain(This,domain) ) + +#define ICoreWebView2Cookie_get_Path(This,path) \ + ( (This)->lpVtbl -> get_Path(This,path) ) + +#define ICoreWebView2Cookie_get_Expires(This,expires) \ + ( (This)->lpVtbl -> get_Expires(This,expires) ) + +#define ICoreWebView2Cookie_put_Expires(This,expires) \ + ( (This)->lpVtbl -> put_Expires(This,expires) ) + +#define ICoreWebView2Cookie_get_IsHttpOnly(This,isHttpOnly) \ + ( (This)->lpVtbl -> get_IsHttpOnly(This,isHttpOnly) ) + +#define ICoreWebView2Cookie_put_IsHttpOnly(This,isHttpOnly) \ + ( (This)->lpVtbl -> put_IsHttpOnly(This,isHttpOnly) ) + +#define ICoreWebView2Cookie_get_SameSite(This,sameSite) \ + ( (This)->lpVtbl -> get_SameSite(This,sameSite) ) + +#define ICoreWebView2Cookie_put_SameSite(This,sameSite) \ + ( (This)->lpVtbl -> put_SameSite(This,sameSite) ) + +#define ICoreWebView2Cookie_get_IsSecure(This,isSecure) \ + ( (This)->lpVtbl -> get_IsSecure(This,isSecure) ) + +#define ICoreWebView2Cookie_put_IsSecure(This,isSecure) \ + ( (This)->lpVtbl -> put_IsSecure(This,isSecure) ) + +#define ICoreWebView2Cookie_get_IsSession(This,isSession) \ + ( (This)->lpVtbl -> get_IsSession(This,isSession) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Cookie_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CookieList_INTERFACE_DEFINED__ +#define __ICoreWebView2CookieList_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CookieList */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CookieList = {0xF7F6F714,0x5D2A,0x43C6,{0x95,0x03,0x34,0x6E,0xCE,0x02,0xD1,0x86}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("F7F6F714-5D2A-43C6-9503-346ECE02D186") + ICoreWebView2CookieList : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Count( + /* [retval][out] */ UINT *count) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetValueAtIndex( + /* [in] */ UINT index, + /* [retval][out] */ ICoreWebView2Cookie **cookie) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CookieListVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CookieList * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CookieList * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CookieList * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Count )( + ICoreWebView2CookieList * This, + /* [retval][out] */ UINT *count); + + HRESULT ( STDMETHODCALLTYPE *GetValueAtIndex )( + ICoreWebView2CookieList * This, + /* [in] */ UINT index, + /* [retval][out] */ ICoreWebView2Cookie **cookie); + + END_INTERFACE + } ICoreWebView2CookieListVtbl; + + interface ICoreWebView2CookieList + { + CONST_VTBL struct ICoreWebView2CookieListVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CookieList_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CookieList_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CookieList_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CookieList_get_Count(This,count) \ + ( (This)->lpVtbl -> get_Count(This,count) ) + +#define ICoreWebView2CookieList_GetValueAtIndex(This,index,cookie) \ + ( (This)->lpVtbl -> GetValueAtIndex(This,index,cookie) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CookieList_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CookieManager_INTERFACE_DEFINED__ +#define __ICoreWebView2CookieManager_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CookieManager */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CookieManager = {0x177CD9E7,0xB6F5,0x451A,{0x94,0xA0,0x5D,0x7A,0x3A,0x4C,0x41,0x41}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("177CD9E7-B6F5-451A-94A0-5D7A3A4C4141") + ICoreWebView2CookieManager : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE CreateCookie( + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR value, + /* [in] */ LPCWSTR domain, + /* [in] */ LPCWSTR path, + /* [retval][out] */ ICoreWebView2Cookie **cookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE CopyCookie( + /* [in] */ ICoreWebView2Cookie *cookieParam, + /* [retval][out] */ ICoreWebView2Cookie **cookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCookies( + /* [in] */ LPCWSTR uri, + /* [in] */ ICoreWebView2GetCookiesCompletedHandler *handler) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddOrUpdateCookie( + /* [in] */ ICoreWebView2Cookie *cookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteCookie( + /* [in] */ ICoreWebView2Cookie *cookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteCookies( + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR uri) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteCookiesWithDomainAndPath( + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR domain, + /* [in] */ LPCWSTR path) = 0; + + virtual HRESULT STDMETHODCALLTYPE DeleteAllCookies( void) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CookieManagerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CookieManager * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CookieManager * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CookieManager * This); + + HRESULT ( STDMETHODCALLTYPE *CreateCookie )( + ICoreWebView2CookieManager * This, + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR value, + /* [in] */ LPCWSTR domain, + /* [in] */ LPCWSTR path, + /* [retval][out] */ ICoreWebView2Cookie **cookie); + + HRESULT ( STDMETHODCALLTYPE *CopyCookie )( + ICoreWebView2CookieManager * This, + /* [in] */ ICoreWebView2Cookie *cookieParam, + /* [retval][out] */ ICoreWebView2Cookie **cookie); + + HRESULT ( STDMETHODCALLTYPE *GetCookies )( + ICoreWebView2CookieManager * This, + /* [in] */ LPCWSTR uri, + /* [in] */ ICoreWebView2GetCookiesCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *AddOrUpdateCookie )( + ICoreWebView2CookieManager * This, + /* [in] */ ICoreWebView2Cookie *cookie); + + HRESULT ( STDMETHODCALLTYPE *DeleteCookie )( + ICoreWebView2CookieManager * This, + /* [in] */ ICoreWebView2Cookie *cookie); + + HRESULT ( STDMETHODCALLTYPE *DeleteCookies )( + ICoreWebView2CookieManager * This, + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR uri); + + HRESULT ( STDMETHODCALLTYPE *DeleteCookiesWithDomainAndPath )( + ICoreWebView2CookieManager * This, + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR domain, + /* [in] */ LPCWSTR path); + + HRESULT ( STDMETHODCALLTYPE *DeleteAllCookies )( + ICoreWebView2CookieManager * This); + + END_INTERFACE + } ICoreWebView2CookieManagerVtbl; + + interface ICoreWebView2CookieManager + { + CONST_VTBL struct ICoreWebView2CookieManagerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CookieManager_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CookieManager_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CookieManager_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CookieManager_CreateCookie(This,name,value,domain,path,cookie) \ + ( (This)->lpVtbl -> CreateCookie(This,name,value,domain,path,cookie) ) + +#define ICoreWebView2CookieManager_CopyCookie(This,cookieParam,cookie) \ + ( (This)->lpVtbl -> CopyCookie(This,cookieParam,cookie) ) + +#define ICoreWebView2CookieManager_GetCookies(This,uri,handler) \ + ( (This)->lpVtbl -> GetCookies(This,uri,handler) ) + +#define ICoreWebView2CookieManager_AddOrUpdateCookie(This,cookie) \ + ( (This)->lpVtbl -> AddOrUpdateCookie(This,cookie) ) + +#define ICoreWebView2CookieManager_DeleteCookie(This,cookie) \ + ( (This)->lpVtbl -> DeleteCookie(This,cookie) ) + +#define ICoreWebView2CookieManager_DeleteCookies(This,name,uri) \ + ( (This)->lpVtbl -> DeleteCookies(This,name,uri) ) + +#define ICoreWebView2CookieManager_DeleteCookiesWithDomainAndPath(This,name,domain,path) \ + ( (This)->lpVtbl -> DeleteCookiesWithDomainAndPath(This,name,domain,path) ) + +#define ICoreWebView2CookieManager_DeleteAllCookies(This) \ + ( (This)->lpVtbl -> DeleteAllCookies(This) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CookieManager_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler = {0x02fab84b,0x1428,0x4fb7,{0xad,0x45,0x1b,0x2e,0x64,0x73,0x61,0x84}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("02fab84b-1428-4fb7-ad45-1b2e64736184") + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + HRESULT errorCode, + ICoreWebView2CompositionController *webView) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler * This, + HRESULT errorCode, + ICoreWebView2CompositionController *webView); + + END_INTERFACE + } ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandlerVtbl; + + interface ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler + { + CONST_VTBL struct ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_Invoke(This,errorCode,webView) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,webView) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CreateCoreWebView2ControllerCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CreateCoreWebView2ControllerCompletedHandler = {0x6c4819f3,0xc9b7,0x4260,{0x81,0x27,0xc9,0xf5,0xbd,0xe7,0xf6,0x8c}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("6c4819f3-c9b7-4260-8127-c9f5bde7f68c") + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + HRESULT errorCode, + ICoreWebView2Controller *createdController) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler * This, + HRESULT errorCode, + ICoreWebView2Controller *createdController); + + END_INTERFACE + } ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl; + + interface ICoreWebView2CreateCoreWebView2ControllerCompletedHandler + { + CONST_VTBL struct ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_Invoke(This,errorCode,createdController) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,createdController) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CreateCoreWebView2ControllerCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler = {0x4e8a3389,0xc9d8,0x4bd2,{0xb6,0xb5,0x12,0x4f,0xee,0x6c,0xc1,0x4d}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("4e8a3389-c9d8-4bd2-b6b5-124fee6cc14d") + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + HRESULT errorCode, + ICoreWebView2Environment *createdEnvironment) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler * This, + HRESULT errorCode, + ICoreWebView2Environment *createdEnvironment); + + END_INTERFACE + } ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl; + + interface ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler + { + CONST_VTBL struct ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_Invoke(This,errorCode,createdEnvironment) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,createdEnvironment) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ContainsFullScreenElementChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2ContainsFullScreenElementChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ContainsFullScreenElementChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ContainsFullScreenElementChangedEventHandler = {0xe45d98b1,0xafef,0x45be,{0x8b,0xaf,0x6c,0x77,0x28,0x86,0x7f,0x73}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("e45d98b1-afef-45be-8baf-6c7728867f73") + ICoreWebView2ContainsFullScreenElementChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ContainsFullScreenElementChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ContainsFullScreenElementChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ContainsFullScreenElementChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ContainsFullScreenElementChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2ContainsFullScreenElementChangedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2ContainsFullScreenElementChangedEventHandlerVtbl; + + interface ICoreWebView2ContainsFullScreenElementChangedEventHandler + { + CONST_VTBL struct ICoreWebView2ContainsFullScreenElementChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ContainsFullScreenElementChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ContainsFullScreenElementChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ContainsFullScreenElementChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ContainsFullScreenElementChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ContainsFullScreenElementChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CursorChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2CursorChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CursorChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CursorChangedEventHandler = {0x9da43ccc,0x26e1,0x4dad,{0xb5,0x6c,0xd8,0x96,0x1c,0x94,0xc5,0x71}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9da43ccc-26e1-4dad-b56c-d8961c94c571") + ICoreWebView2CursorChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2CompositionController *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CursorChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CursorChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CursorChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CursorChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2CursorChangedEventHandler * This, + /* [in] */ ICoreWebView2CompositionController *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2CursorChangedEventHandlerVtbl; + + interface ICoreWebView2CursorChangedEventHandler + { + CONST_VTBL struct ICoreWebView2CursorChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CursorChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CursorChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CursorChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CursorChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CursorChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2DocumentTitleChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2DocumentTitleChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2DocumentTitleChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2DocumentTitleChangedEventHandler = {0xf5f2b923,0x953e,0x4042,{0x9f,0x95,0xf3,0xa1,0x18,0xe1,0xaf,0xd4}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("f5f2b923-953e-4042-9f95-f3a118e1afd4") + ICoreWebView2DocumentTitleChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2DocumentTitleChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2DocumentTitleChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2DocumentTitleChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2DocumentTitleChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2DocumentTitleChangedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2DocumentTitleChangedEventHandlerVtbl; + + interface ICoreWebView2DocumentTitleChangedEventHandler + { + CONST_VTBL struct ICoreWebView2DocumentTitleChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2DocumentTitleChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2DocumentTitleChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2DocumentTitleChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2DocumentTitleChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2DocumentTitleChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2DOMContentLoadedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2DOMContentLoadedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2DOMContentLoadedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2DOMContentLoadedEventArgs = {0x16B1E21A,0xC503,0x44F2,{0x84,0xC9,0x70,0xAB,0xA5,0x03,0x12,0x83}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("16B1E21A-C503-44F2-84C9-70ABA5031283") + ICoreWebView2DOMContentLoadedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_NavigationId( + /* [retval][out] */ UINT64 *navigationId) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2DOMContentLoadedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2DOMContentLoadedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2DOMContentLoadedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2DOMContentLoadedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_NavigationId )( + ICoreWebView2DOMContentLoadedEventArgs * This, + /* [retval][out] */ UINT64 *navigationId); + + END_INTERFACE + } ICoreWebView2DOMContentLoadedEventArgsVtbl; + + interface ICoreWebView2DOMContentLoadedEventArgs + { + CONST_VTBL struct ICoreWebView2DOMContentLoadedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2DOMContentLoadedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2DOMContentLoadedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2DOMContentLoadedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2DOMContentLoadedEventArgs_get_NavigationId(This,navigationId) \ + ( (This)->lpVtbl -> get_NavigationId(This,navigationId) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2DOMContentLoadedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2DOMContentLoadedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2DOMContentLoadedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2DOMContentLoadedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2DOMContentLoadedEventHandler = {0x4BAC7E9C,0x199E,0x49ED,{0x87,0xED,0x24,0x93,0x03,0xAC,0xF0,0x19}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("4BAC7E9C-199E-49ED-87ED-249303ACF019") + ICoreWebView2DOMContentLoadedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2DOMContentLoadedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2DOMContentLoadedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2DOMContentLoadedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2DOMContentLoadedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2DOMContentLoadedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2DOMContentLoadedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2DOMContentLoadedEventArgs *args); + + END_INTERFACE + } ICoreWebView2DOMContentLoadedEventHandlerVtbl; + + interface ICoreWebView2DOMContentLoadedEventHandler + { + CONST_VTBL struct ICoreWebView2DOMContentLoadedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2DOMContentLoadedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2DOMContentLoadedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2DOMContentLoadedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2DOMContentLoadedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2DOMContentLoadedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Deferral_INTERFACE_DEFINED__ +#define __ICoreWebView2Deferral_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Deferral */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Deferral = {0xc10e7f7b,0xb585,0x46f0,{0xa6,0x23,0x8b,0xef,0xbf,0x3e,0x4e,0xe0}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("c10e7f7b-b585-46f0-a623-8befbf3e4ee0") + ICoreWebView2Deferral : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Complete( void) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2DeferralVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Deferral * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Deferral * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Deferral * This); + + HRESULT ( STDMETHODCALLTYPE *Complete )( + ICoreWebView2Deferral * This); + + END_INTERFACE + } ICoreWebView2DeferralVtbl; + + interface ICoreWebView2Deferral + { + CONST_VTBL struct ICoreWebView2DeferralVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Deferral_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Deferral_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Deferral_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Deferral_Complete(This) \ + ( (This)->lpVtbl -> Complete(This) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Deferral_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2DevToolsProtocolEventReceivedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2DevToolsProtocolEventReceivedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2DevToolsProtocolEventReceivedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2DevToolsProtocolEventReceivedEventArgs = {0x653c2959,0xbb3a,0x4377,{0x86,0x32,0xb5,0x8a,0xda,0x4e,0x66,0xc4}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("653c2959-bb3a-4377-8632-b58ada4e66c4") + ICoreWebView2DevToolsProtocolEventReceivedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ParameterObjectAsJson( + /* [retval][out] */ LPWSTR *parameterObjectAsJson) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2DevToolsProtocolEventReceivedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2DevToolsProtocolEventReceivedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2DevToolsProtocolEventReceivedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2DevToolsProtocolEventReceivedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ParameterObjectAsJson )( + ICoreWebView2DevToolsProtocolEventReceivedEventArgs * This, + /* [retval][out] */ LPWSTR *parameterObjectAsJson); + + END_INTERFACE + } ICoreWebView2DevToolsProtocolEventReceivedEventArgsVtbl; + + interface ICoreWebView2DevToolsProtocolEventReceivedEventArgs + { + CONST_VTBL struct ICoreWebView2DevToolsProtocolEventReceivedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2DevToolsProtocolEventReceivedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2DevToolsProtocolEventReceivedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2DevToolsProtocolEventReceivedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2DevToolsProtocolEventReceivedEventArgs_get_ParameterObjectAsJson(This,parameterObjectAsJson) \ + ( (This)->lpVtbl -> get_ParameterObjectAsJson(This,parameterObjectAsJson) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2DevToolsProtocolEventReceivedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2DevToolsProtocolEventReceivedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2DevToolsProtocolEventReceivedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2DevToolsProtocolEventReceivedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2DevToolsProtocolEventReceivedEventHandler = {0xe2fda4be,0x5456,0x406c,{0xa2,0x61,0x3d,0x45,0x21,0x38,0x36,0x2c}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("e2fda4be-5456-406c-a261-3d452138362c") + ICoreWebView2DevToolsProtocolEventReceivedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2DevToolsProtocolEventReceivedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2DevToolsProtocolEventReceivedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2DevToolsProtocolEventReceivedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2DevToolsProtocolEventReceivedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2DevToolsProtocolEventReceivedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2DevToolsProtocolEventReceivedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2DevToolsProtocolEventReceivedEventArgs *args); + + END_INTERFACE + } ICoreWebView2DevToolsProtocolEventReceivedEventHandlerVtbl; + + interface ICoreWebView2DevToolsProtocolEventReceivedEventHandler + { + CONST_VTBL struct ICoreWebView2DevToolsProtocolEventReceivedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2DevToolsProtocolEventReceivedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2DevToolsProtocolEventReceivedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2DevToolsProtocolEventReceivedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2DevToolsProtocolEventReceivedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2DevToolsProtocolEventReceivedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2DevToolsProtocolEventReceiver_INTERFACE_DEFINED__ +#define __ICoreWebView2DevToolsProtocolEventReceiver_INTERFACE_DEFINED__ + +/* interface ICoreWebView2DevToolsProtocolEventReceiver */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2DevToolsProtocolEventReceiver = {0xb32ca51a,0x8371,0x45e9,{0x93,0x17,0xaf,0x02,0x1d,0x08,0x03,0x67}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("b32ca51a-8371-45e9-9317-af021d080367") + ICoreWebView2DevToolsProtocolEventReceiver : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE add_DevToolsProtocolEventReceived( + /* [in] */ ICoreWebView2DevToolsProtocolEventReceivedEventHandler *handler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_DevToolsProtocolEventReceived( + /* [in] */ EventRegistrationToken token) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2DevToolsProtocolEventReceiverVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2DevToolsProtocolEventReceiver * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2DevToolsProtocolEventReceiver * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2DevToolsProtocolEventReceiver * This); + + HRESULT ( STDMETHODCALLTYPE *add_DevToolsProtocolEventReceived )( + ICoreWebView2DevToolsProtocolEventReceiver * This, + /* [in] */ ICoreWebView2DevToolsProtocolEventReceivedEventHandler *handler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_DevToolsProtocolEventReceived )( + ICoreWebView2DevToolsProtocolEventReceiver * This, + /* [in] */ EventRegistrationToken token); + + END_INTERFACE + } ICoreWebView2DevToolsProtocolEventReceiverVtbl; + + interface ICoreWebView2DevToolsProtocolEventReceiver + { + CONST_VTBL struct ICoreWebView2DevToolsProtocolEventReceiverVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2DevToolsProtocolEventReceiver_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2DevToolsProtocolEventReceiver_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2DevToolsProtocolEventReceiver_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2DevToolsProtocolEventReceiver_add_DevToolsProtocolEventReceived(This,handler,token) \ + ( (This)->lpVtbl -> add_DevToolsProtocolEventReceived(This,handler,token) ) + +#define ICoreWebView2DevToolsProtocolEventReceiver_remove_DevToolsProtocolEventReceived(This,token) \ + ( (This)->lpVtbl -> remove_DevToolsProtocolEventReceived(This,token) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2DevToolsProtocolEventReceiver_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment_INTERFACE_DEFINED__ +#define __ICoreWebView2Environment_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Environment */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Environment = {0xb96d755e,0x0319,0x4e92,{0xa2,0x96,0x23,0x43,0x6f,0x46,0xa1,0xfc}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("b96d755e-0319-4e92-a296-23436f46a1fc") + ICoreWebView2Environment : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE CreateCoreWebView2Controller( + HWND parentWindow, + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler *handler) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateWebResourceResponse( + /* [in] */ IStream *content, + /* [in] */ int statusCode, + /* [in] */ LPCWSTR reasonPhrase, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceResponse **response) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_BrowserVersionString( + /* [retval][out] */ LPWSTR *versionInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE add_NewBrowserVersionAvailable( + /* [in] */ ICoreWebView2NewBrowserVersionAvailableEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token) = 0; + + virtual HRESULT STDMETHODCALLTYPE remove_NewBrowserVersionAvailable( + /* [in] */ EventRegistrationToken token) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2EnvironmentVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Environment * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Environment * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Environment * This); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2Controller )( + ICoreWebView2Environment * This, + HWND parentWindow, + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CreateWebResourceResponse )( + ICoreWebView2Environment * This, + /* [in] */ IStream *content, + /* [in] */ int statusCode, + /* [in] */ LPCWSTR reasonPhrase, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceResponse **response); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BrowserVersionString )( + ICoreWebView2Environment * This, + /* [retval][out] */ LPWSTR *versionInfo); + + HRESULT ( STDMETHODCALLTYPE *add_NewBrowserVersionAvailable )( + ICoreWebView2Environment * This, + /* [in] */ ICoreWebView2NewBrowserVersionAvailableEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NewBrowserVersionAvailable )( + ICoreWebView2Environment * This, + /* [in] */ EventRegistrationToken token); + + END_INTERFACE + } ICoreWebView2EnvironmentVtbl; + + interface ICoreWebView2Environment + { + CONST_VTBL struct ICoreWebView2EnvironmentVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Environment_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Environment_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Environment_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Environment_CreateCoreWebView2Controller(This,parentWindow,handler) \ + ( (This)->lpVtbl -> CreateCoreWebView2Controller(This,parentWindow,handler) ) + +#define ICoreWebView2Environment_CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) \ + ( (This)->lpVtbl -> CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) ) + +#define ICoreWebView2Environment_get_BrowserVersionString(This,versionInfo) \ + ( (This)->lpVtbl -> get_BrowserVersionString(This,versionInfo) ) + +#define ICoreWebView2Environment_add_NewBrowserVersionAvailable(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NewBrowserVersionAvailable(This,eventHandler,token) ) + +#define ICoreWebView2Environment_remove_NewBrowserVersionAvailable(This,token) \ + ( (This)->lpVtbl -> remove_NewBrowserVersionAvailable(This,token) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Environment_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment2_INTERFACE_DEFINED__ +#define __ICoreWebView2Environment2_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Environment2 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Environment2 = {0x41F3632B,0x5EF4,0x404F,{0xAD,0x82,0x2D,0x60,0x6C,0x5A,0x9A,0x21}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("41F3632B-5EF4-404F-AD82-2D606C5A9A21") + ICoreWebView2Environment2 : public ICoreWebView2Environment + { + public: + virtual HRESULT STDMETHODCALLTYPE CreateWebResourceRequest( + /* [in] */ LPCWSTR uri, + /* [in] */ LPCWSTR method, + /* [in] */ IStream *postData, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceRequest **request) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Environment2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Environment2 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Environment2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Environment2 * This); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2Controller )( + ICoreWebView2Environment2 * This, + HWND parentWindow, + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CreateWebResourceResponse )( + ICoreWebView2Environment2 * This, + /* [in] */ IStream *content, + /* [in] */ int statusCode, + /* [in] */ LPCWSTR reasonPhrase, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceResponse **response); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BrowserVersionString )( + ICoreWebView2Environment2 * This, + /* [retval][out] */ LPWSTR *versionInfo); + + HRESULT ( STDMETHODCALLTYPE *add_NewBrowserVersionAvailable )( + ICoreWebView2Environment2 * This, + /* [in] */ ICoreWebView2NewBrowserVersionAvailableEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NewBrowserVersionAvailable )( + ICoreWebView2Environment2 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *CreateWebResourceRequest )( + ICoreWebView2Environment2 * This, + /* [in] */ LPCWSTR uri, + /* [in] */ LPCWSTR method, + /* [in] */ IStream *postData, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceRequest **request); + + END_INTERFACE + } ICoreWebView2Environment2Vtbl; + + interface ICoreWebView2Environment2 + { + CONST_VTBL struct ICoreWebView2Environment2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Environment2_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Environment2_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Environment2_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Environment2_CreateCoreWebView2Controller(This,parentWindow,handler) \ + ( (This)->lpVtbl -> CreateCoreWebView2Controller(This,parentWindow,handler) ) + +#define ICoreWebView2Environment2_CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) \ + ( (This)->lpVtbl -> CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) ) + +#define ICoreWebView2Environment2_get_BrowserVersionString(This,versionInfo) \ + ( (This)->lpVtbl -> get_BrowserVersionString(This,versionInfo) ) + +#define ICoreWebView2Environment2_add_NewBrowserVersionAvailable(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NewBrowserVersionAvailable(This,eventHandler,token) ) + +#define ICoreWebView2Environment2_remove_NewBrowserVersionAvailable(This,token) \ + ( (This)->lpVtbl -> remove_NewBrowserVersionAvailable(This,token) ) + + +#define ICoreWebView2Environment2_CreateWebResourceRequest(This,uri,method,postData,headers,request) \ + ( (This)->lpVtbl -> CreateWebResourceRequest(This,uri,method,postData,headers,request) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Environment2_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment3_INTERFACE_DEFINED__ +#define __ICoreWebView2Environment3_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Environment3 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Environment3 = {0x80a22ae3,0xbe7c,0x4ce2,{0xaf,0xe1,0x5a,0x50,0x05,0x6c,0xde,0xeb}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("80a22ae3-be7c-4ce2-afe1-5a50056cdeeb") + ICoreWebView2Environment3 : public ICoreWebView2Environment2 + { + public: + virtual HRESULT STDMETHODCALLTYPE CreateCoreWebView2CompositionController( + HWND parentWindow, + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler *handler) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateCoreWebView2PointerInfo( + /* [retval][out] */ ICoreWebView2PointerInfo **pointerInfo) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Environment3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Environment3 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Environment3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Environment3 * This); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2Controller )( + ICoreWebView2Environment3 * This, + HWND parentWindow, + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CreateWebResourceResponse )( + ICoreWebView2Environment3 * This, + /* [in] */ IStream *content, + /* [in] */ int statusCode, + /* [in] */ LPCWSTR reasonPhrase, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceResponse **response); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BrowserVersionString )( + ICoreWebView2Environment3 * This, + /* [retval][out] */ LPWSTR *versionInfo); + + HRESULT ( STDMETHODCALLTYPE *add_NewBrowserVersionAvailable )( + ICoreWebView2Environment3 * This, + /* [in] */ ICoreWebView2NewBrowserVersionAvailableEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NewBrowserVersionAvailable )( + ICoreWebView2Environment3 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *CreateWebResourceRequest )( + ICoreWebView2Environment3 * This, + /* [in] */ LPCWSTR uri, + /* [in] */ LPCWSTR method, + /* [in] */ IStream *postData, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceRequest **request); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2CompositionController )( + ICoreWebView2Environment3 * This, + HWND parentWindow, + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2PointerInfo )( + ICoreWebView2Environment3 * This, + /* [retval][out] */ ICoreWebView2PointerInfo **pointerInfo); + + END_INTERFACE + } ICoreWebView2Environment3Vtbl; + + interface ICoreWebView2Environment3 + { + CONST_VTBL struct ICoreWebView2Environment3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Environment3_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Environment3_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Environment3_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Environment3_CreateCoreWebView2Controller(This,parentWindow,handler) \ + ( (This)->lpVtbl -> CreateCoreWebView2Controller(This,parentWindow,handler) ) + +#define ICoreWebView2Environment3_CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) \ + ( (This)->lpVtbl -> CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) ) + +#define ICoreWebView2Environment3_get_BrowserVersionString(This,versionInfo) \ + ( (This)->lpVtbl -> get_BrowserVersionString(This,versionInfo) ) + +#define ICoreWebView2Environment3_add_NewBrowserVersionAvailable(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NewBrowserVersionAvailable(This,eventHandler,token) ) + +#define ICoreWebView2Environment3_remove_NewBrowserVersionAvailable(This,token) \ + ( (This)->lpVtbl -> remove_NewBrowserVersionAvailable(This,token) ) + + +#define ICoreWebView2Environment3_CreateWebResourceRequest(This,uri,method,postData,headers,request) \ + ( (This)->lpVtbl -> CreateWebResourceRequest(This,uri,method,postData,headers,request) ) + + +#define ICoreWebView2Environment3_CreateCoreWebView2CompositionController(This,parentWindow,handler) \ + ( (This)->lpVtbl -> CreateCoreWebView2CompositionController(This,parentWindow,handler) ) + +#define ICoreWebView2Environment3_CreateCoreWebView2PointerInfo(This,pointerInfo) \ + ( (This)->lpVtbl -> CreateCoreWebView2PointerInfo(This,pointerInfo) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Environment3_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Environment4_INTERFACE_DEFINED__ +#define __ICoreWebView2Environment4_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Environment4 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Environment4 = {0x20944379,0x6dcf,0x41d6,{0xa0,0xa0,0xab,0xc0,0xfc,0x50,0xde,0x0d}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("20944379-6dcf-41d6-a0a0-abc0fc50de0d") + ICoreWebView2Environment4 : public ICoreWebView2Environment3 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetProviderForHwnd( + /* [in] */ HWND hwnd, + /* [retval][out] */ IUnknown **provider) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Environment4Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Environment4 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Environment4 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Environment4 * This); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2Controller )( + ICoreWebView2Environment4 * This, + HWND parentWindow, + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CreateWebResourceResponse )( + ICoreWebView2Environment4 * This, + /* [in] */ IStream *content, + /* [in] */ int statusCode, + /* [in] */ LPCWSTR reasonPhrase, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceResponse **response); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_BrowserVersionString )( + ICoreWebView2Environment4 * This, + /* [retval][out] */ LPWSTR *versionInfo); + + HRESULT ( STDMETHODCALLTYPE *add_NewBrowserVersionAvailable )( + ICoreWebView2Environment4 * This, + /* [in] */ ICoreWebView2NewBrowserVersionAvailableEventHandler *eventHandler, + /* [out] */ EventRegistrationToken *token); + + HRESULT ( STDMETHODCALLTYPE *remove_NewBrowserVersionAvailable )( + ICoreWebView2Environment4 * This, + /* [in] */ EventRegistrationToken token); + + HRESULT ( STDMETHODCALLTYPE *CreateWebResourceRequest )( + ICoreWebView2Environment4 * This, + /* [in] */ LPCWSTR uri, + /* [in] */ LPCWSTR method, + /* [in] */ IStream *postData, + /* [in] */ LPCWSTR headers, + /* [retval][out] */ ICoreWebView2WebResourceRequest **request); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2CompositionController )( + ICoreWebView2Environment4 * This, + HWND parentWindow, + ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler *handler); + + HRESULT ( STDMETHODCALLTYPE *CreateCoreWebView2PointerInfo )( + ICoreWebView2Environment4 * This, + /* [retval][out] */ ICoreWebView2PointerInfo **pointerInfo); + + HRESULT ( STDMETHODCALLTYPE *GetProviderForHwnd )( + ICoreWebView2Environment4 * This, + /* [in] */ HWND hwnd, + /* [retval][out] */ IUnknown **provider); + + END_INTERFACE + } ICoreWebView2Environment4Vtbl; + + interface ICoreWebView2Environment4 + { + CONST_VTBL struct ICoreWebView2Environment4Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Environment4_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Environment4_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Environment4_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Environment4_CreateCoreWebView2Controller(This,parentWindow,handler) \ + ( (This)->lpVtbl -> CreateCoreWebView2Controller(This,parentWindow,handler) ) + +#define ICoreWebView2Environment4_CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) \ + ( (This)->lpVtbl -> CreateWebResourceResponse(This,content,statusCode,reasonPhrase,headers,response) ) + +#define ICoreWebView2Environment4_get_BrowserVersionString(This,versionInfo) \ + ( (This)->lpVtbl -> get_BrowserVersionString(This,versionInfo) ) + +#define ICoreWebView2Environment4_add_NewBrowserVersionAvailable(This,eventHandler,token) \ + ( (This)->lpVtbl -> add_NewBrowserVersionAvailable(This,eventHandler,token) ) + +#define ICoreWebView2Environment4_remove_NewBrowserVersionAvailable(This,token) \ + ( (This)->lpVtbl -> remove_NewBrowserVersionAvailable(This,token) ) + + +#define ICoreWebView2Environment4_CreateWebResourceRequest(This,uri,method,postData,headers,request) \ + ( (This)->lpVtbl -> CreateWebResourceRequest(This,uri,method,postData,headers,request) ) + + +#define ICoreWebView2Environment4_CreateCoreWebView2CompositionController(This,parentWindow,handler) \ + ( (This)->lpVtbl -> CreateCoreWebView2CompositionController(This,parentWindow,handler) ) + +#define ICoreWebView2Environment4_CreateCoreWebView2PointerInfo(This,pointerInfo) \ + ( (This)->lpVtbl -> CreateCoreWebView2PointerInfo(This,pointerInfo) ) + + +#define ICoreWebView2Environment4_GetProviderForHwnd(This,hwnd,provider) \ + ( (This)->lpVtbl -> GetProviderForHwnd(This,hwnd,provider) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Environment4_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2EnvironmentOptions_INTERFACE_DEFINED__ +#define __ICoreWebView2EnvironmentOptions_INTERFACE_DEFINED__ + +/* interface ICoreWebView2EnvironmentOptions */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2EnvironmentOptions = {0x2fde08a8,0x1e9a,0x4766,{0x8c,0x05,0x95,0xa9,0xce,0xb9,0xd1,0xc5}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("2fde08a8-1e9a-4766-8c05-95a9ceb9d1c5") + ICoreWebView2EnvironmentOptions : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_AdditionalBrowserArguments( + /* [retval][out] */ LPWSTR *value) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_AdditionalBrowserArguments( + /* [in] */ LPCWSTR value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Language( + /* [retval][out] */ LPWSTR *value) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Language( + /* [in] */ LPCWSTR value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_TargetCompatibleBrowserVersion( + /* [retval][out] */ LPWSTR *value) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_TargetCompatibleBrowserVersion( + /* [in] */ LPCWSTR value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_AllowSingleSignOnUsingOSPrimaryAccount( + /* [retval][out] */ BOOL *allow) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_AllowSingleSignOnUsingOSPrimaryAccount( + /* [in] */ BOOL allow) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2EnvironmentOptionsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2EnvironmentOptions * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2EnvironmentOptions * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2EnvironmentOptions * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AdditionalBrowserArguments )( + ICoreWebView2EnvironmentOptions * This, + /* [retval][out] */ LPWSTR *value); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AdditionalBrowserArguments )( + ICoreWebView2EnvironmentOptions * This, + /* [in] */ LPCWSTR value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Language )( + ICoreWebView2EnvironmentOptions * This, + /* [retval][out] */ LPWSTR *value); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Language )( + ICoreWebView2EnvironmentOptions * This, + /* [in] */ LPCWSTR value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_TargetCompatibleBrowserVersion )( + ICoreWebView2EnvironmentOptions * This, + /* [retval][out] */ LPWSTR *value); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_TargetCompatibleBrowserVersion )( + ICoreWebView2EnvironmentOptions * This, + /* [in] */ LPCWSTR value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AllowSingleSignOnUsingOSPrimaryAccount )( + ICoreWebView2EnvironmentOptions * This, + /* [retval][out] */ BOOL *allow); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AllowSingleSignOnUsingOSPrimaryAccount )( + ICoreWebView2EnvironmentOptions * This, + /* [in] */ BOOL allow); + + END_INTERFACE + } ICoreWebView2EnvironmentOptionsVtbl; + + interface ICoreWebView2EnvironmentOptions + { + CONST_VTBL struct ICoreWebView2EnvironmentOptionsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2EnvironmentOptions_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2EnvironmentOptions_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2EnvironmentOptions_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2EnvironmentOptions_get_AdditionalBrowserArguments(This,value) \ + ( (This)->lpVtbl -> get_AdditionalBrowserArguments(This,value) ) + +#define ICoreWebView2EnvironmentOptions_put_AdditionalBrowserArguments(This,value) \ + ( (This)->lpVtbl -> put_AdditionalBrowserArguments(This,value) ) + +#define ICoreWebView2EnvironmentOptions_get_Language(This,value) \ + ( (This)->lpVtbl -> get_Language(This,value) ) + +#define ICoreWebView2EnvironmentOptions_put_Language(This,value) \ + ( (This)->lpVtbl -> put_Language(This,value) ) + +#define ICoreWebView2EnvironmentOptions_get_TargetCompatibleBrowserVersion(This,value) \ + ( (This)->lpVtbl -> get_TargetCompatibleBrowserVersion(This,value) ) + +#define ICoreWebView2EnvironmentOptions_put_TargetCompatibleBrowserVersion(This,value) \ + ( (This)->lpVtbl -> put_TargetCompatibleBrowserVersion(This,value) ) + +#define ICoreWebView2EnvironmentOptions_get_AllowSingleSignOnUsingOSPrimaryAccount(This,allow) \ + ( (This)->lpVtbl -> get_AllowSingleSignOnUsingOSPrimaryAccount(This,allow) ) + +#define ICoreWebView2EnvironmentOptions_put_AllowSingleSignOnUsingOSPrimaryAccount(This,allow) \ + ( (This)->lpVtbl -> put_AllowSingleSignOnUsingOSPrimaryAccount(This,allow) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2EnvironmentOptions_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ExecuteScriptCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2ExecuteScriptCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ExecuteScriptCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ExecuteScriptCompletedHandler = {0x49511172,0xcc67,0x4bca,{0x99,0x23,0x13,0x71,0x12,0xf4,0xc4,0xcc}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("49511172-cc67-4bca-9923-137112f4c4cc") + ICoreWebView2ExecuteScriptCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ HRESULT errorCode, + /* [in] */ LPCWSTR resultObjectAsJson) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ExecuteScriptCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ExecuteScriptCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ExecuteScriptCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ExecuteScriptCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2ExecuteScriptCompletedHandler * This, + /* [in] */ HRESULT errorCode, + /* [in] */ LPCWSTR resultObjectAsJson); + + END_INTERFACE + } ICoreWebView2ExecuteScriptCompletedHandlerVtbl; + + interface ICoreWebView2ExecuteScriptCompletedHandler + { + CONST_VTBL struct ICoreWebView2ExecuteScriptCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ExecuteScriptCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ExecuteScriptCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ExecuteScriptCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ExecuteScriptCompletedHandler_Invoke(This,errorCode,resultObjectAsJson) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,resultObjectAsJson) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ExecuteScriptCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2FrameInfo_INTERFACE_DEFINED__ +#define __ICoreWebView2FrameInfo_INTERFACE_DEFINED__ + +/* interface ICoreWebView2FrameInfo */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2FrameInfo = {0xda86b8a1,0xbdf3,0x4f11,{0x99,0x55,0x52,0x8c,0xef,0xa5,0x97,0x27}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("da86b8a1-bdf3-4f11-9955-528cefa59727") + ICoreWebView2FrameInfo : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Name( + /* [retval][out] */ LPWSTR *name) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Source( + /* [retval][out] */ LPWSTR *source) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2FrameInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2FrameInfo * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2FrameInfo * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2FrameInfo * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Name )( + ICoreWebView2FrameInfo * This, + /* [retval][out] */ LPWSTR *name); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Source )( + ICoreWebView2FrameInfo * This, + /* [retval][out] */ LPWSTR *source); + + END_INTERFACE + } ICoreWebView2FrameInfoVtbl; + + interface ICoreWebView2FrameInfo + { + CONST_VTBL struct ICoreWebView2FrameInfoVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2FrameInfo_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2FrameInfo_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2FrameInfo_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2FrameInfo_get_Name(This,name) \ + ( (This)->lpVtbl -> get_Name(This,name) ) + +#define ICoreWebView2FrameInfo_get_Source(This,source) \ + ( (This)->lpVtbl -> get_Source(This,source) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2FrameInfo_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2FrameInfoCollection_INTERFACE_DEFINED__ +#define __ICoreWebView2FrameInfoCollection_INTERFACE_DEFINED__ + +/* interface ICoreWebView2FrameInfoCollection */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2FrameInfoCollection = {0x8f834154,0xd38e,0x4d90,{0xaf,0xfb,0x68,0x00,0xa7,0x27,0x28,0x39}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("8f834154-d38e-4d90-affb-6800a7272839") + ICoreWebView2FrameInfoCollection : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetIterator( + /* [retval][out] */ ICoreWebView2FrameInfoCollectionIterator **iterator) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2FrameInfoCollectionVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2FrameInfoCollection * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2FrameInfoCollection * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2FrameInfoCollection * This); + + HRESULT ( STDMETHODCALLTYPE *GetIterator )( + ICoreWebView2FrameInfoCollection * This, + /* [retval][out] */ ICoreWebView2FrameInfoCollectionIterator **iterator); + + END_INTERFACE + } ICoreWebView2FrameInfoCollectionVtbl; + + interface ICoreWebView2FrameInfoCollection + { + CONST_VTBL struct ICoreWebView2FrameInfoCollectionVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2FrameInfoCollection_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2FrameInfoCollection_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2FrameInfoCollection_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2FrameInfoCollection_GetIterator(This,iterator) \ + ( (This)->lpVtbl -> GetIterator(This,iterator) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2FrameInfoCollection_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2FrameInfoCollectionIterator_INTERFACE_DEFINED__ +#define __ICoreWebView2FrameInfoCollectionIterator_INTERFACE_DEFINED__ + +/* interface ICoreWebView2FrameInfoCollectionIterator */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2FrameInfoCollectionIterator = {0x1bf89e2d,0x1b2b,0x4629,{0xb2,0x8f,0x05,0x09,0x9b,0x41,0xbb,0x03}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("1bf89e2d-1b2b-4629-b28f-05099b41bb03") + ICoreWebView2FrameInfoCollectionIterator : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HasCurrent( + /* [retval][out] */ BOOL *hasCurrent) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrent( + /* [retval][out] */ ICoreWebView2FrameInfo **frameInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE MoveNext( + /* [retval][out] */ BOOL *hasNext) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2FrameInfoCollectionIteratorVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2FrameInfoCollectionIterator * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2FrameInfoCollectionIterator * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2FrameInfoCollectionIterator * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HasCurrent )( + ICoreWebView2FrameInfoCollectionIterator * This, + /* [retval][out] */ BOOL *hasCurrent); + + HRESULT ( STDMETHODCALLTYPE *GetCurrent )( + ICoreWebView2FrameInfoCollectionIterator * This, + /* [retval][out] */ ICoreWebView2FrameInfo **frameInfo); + + HRESULT ( STDMETHODCALLTYPE *MoveNext )( + ICoreWebView2FrameInfoCollectionIterator * This, + /* [retval][out] */ BOOL *hasNext); + + END_INTERFACE + } ICoreWebView2FrameInfoCollectionIteratorVtbl; + + interface ICoreWebView2FrameInfoCollectionIterator + { + CONST_VTBL struct ICoreWebView2FrameInfoCollectionIteratorVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2FrameInfoCollectionIterator_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2FrameInfoCollectionIterator_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2FrameInfoCollectionIterator_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2FrameInfoCollectionIterator_get_HasCurrent(This,hasCurrent) \ + ( (This)->lpVtbl -> get_HasCurrent(This,hasCurrent) ) + +#define ICoreWebView2FrameInfoCollectionIterator_GetCurrent(This,frameInfo) \ + ( (This)->lpVtbl -> GetCurrent(This,frameInfo) ) + +#define ICoreWebView2FrameInfoCollectionIterator_MoveNext(This,hasNext) \ + ( (This)->lpVtbl -> MoveNext(This,hasNext) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2FrameInfoCollectionIterator_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2FocusChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2FocusChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2FocusChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2FocusChangedEventHandler = {0x05ea24bd,0x6452,0x4926,{0x90,0x14,0x4b,0x82,0xb4,0x98,0x13,0x5d}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("05ea24bd-6452-4926-9014-4b82b498135d") + ICoreWebView2FocusChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2FocusChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2FocusChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2FocusChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2FocusChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2FocusChangedEventHandler * This, + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2FocusChangedEventHandlerVtbl; + + interface ICoreWebView2FocusChangedEventHandler + { + CONST_VTBL struct ICoreWebView2FocusChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2FocusChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2FocusChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2FocusChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2FocusChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2FocusChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2GetCookiesCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2GetCookiesCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2GetCookiesCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2GetCookiesCompletedHandler = {0x5A4F5069,0x5C15,0x47C3,{0x86,0x46,0xF4,0xDE,0x1C,0x11,0x66,0x70}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("5A4F5069-5C15-47C3-8646-F4DE1C116670") + ICoreWebView2GetCookiesCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + HRESULT result, + ICoreWebView2CookieList *cookieList) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2GetCookiesCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2GetCookiesCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2GetCookiesCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2GetCookiesCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2GetCookiesCompletedHandler * This, + HRESULT result, + ICoreWebView2CookieList *cookieList); + + END_INTERFACE + } ICoreWebView2GetCookiesCompletedHandlerVtbl; + + interface ICoreWebView2GetCookiesCompletedHandler + { + CONST_VTBL struct ICoreWebView2GetCookiesCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2GetCookiesCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2GetCookiesCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2GetCookiesCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2GetCookiesCompletedHandler_Invoke(This,result,cookieList) \ + ( (This)->lpVtbl -> Invoke(This,result,cookieList) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2GetCookiesCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2HistoryChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2HistoryChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2HistoryChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2HistoryChangedEventHandler = {0xc79a420c,0xefd9,0x4058,{0x92,0x95,0x3e,0x8b,0x4b,0xca,0xb6,0x45}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("c79a420c-efd9-4058-9295-3e8b4bcab645") + ICoreWebView2HistoryChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2HistoryChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2HistoryChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2HistoryChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2HistoryChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2HistoryChangedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2HistoryChangedEventHandlerVtbl; + + interface ICoreWebView2HistoryChangedEventHandler + { + CONST_VTBL struct ICoreWebView2HistoryChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2HistoryChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2HistoryChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2HistoryChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2HistoryChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2HistoryChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2HttpHeadersCollectionIterator_INTERFACE_DEFINED__ +#define __ICoreWebView2HttpHeadersCollectionIterator_INTERFACE_DEFINED__ + +/* interface ICoreWebView2HttpHeadersCollectionIterator */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2HttpHeadersCollectionIterator = {0x0702fc30,0xf43b,0x47bb,{0xab,0x52,0xa4,0x2c,0xb5,0x52,0xad,0x9f}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("0702fc30-f43b-47bb-ab52-a42cb552ad9f") + ICoreWebView2HttpHeadersCollectionIterator : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetCurrentHeader( + /* [out] */ LPWSTR *name, + /* [out] */ LPWSTR *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HasCurrentHeader( + /* [retval][out] */ BOOL *hasCurrent) = 0; + + virtual HRESULT STDMETHODCALLTYPE MoveNext( + /* [retval][out] */ BOOL *hasNext) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2HttpHeadersCollectionIteratorVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2HttpHeadersCollectionIterator * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2HttpHeadersCollectionIterator * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2HttpHeadersCollectionIterator * This); + + HRESULT ( STDMETHODCALLTYPE *GetCurrentHeader )( + ICoreWebView2HttpHeadersCollectionIterator * This, + /* [out] */ LPWSTR *name, + /* [out] */ LPWSTR *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HasCurrentHeader )( + ICoreWebView2HttpHeadersCollectionIterator * This, + /* [retval][out] */ BOOL *hasCurrent); + + HRESULT ( STDMETHODCALLTYPE *MoveNext )( + ICoreWebView2HttpHeadersCollectionIterator * This, + /* [retval][out] */ BOOL *hasNext); + + END_INTERFACE + } ICoreWebView2HttpHeadersCollectionIteratorVtbl; + + interface ICoreWebView2HttpHeadersCollectionIterator + { + CONST_VTBL struct ICoreWebView2HttpHeadersCollectionIteratorVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2HttpHeadersCollectionIterator_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2HttpHeadersCollectionIterator_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2HttpHeadersCollectionIterator_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2HttpHeadersCollectionIterator_GetCurrentHeader(This,name,value) \ + ( (This)->lpVtbl -> GetCurrentHeader(This,name,value) ) + +#define ICoreWebView2HttpHeadersCollectionIterator_get_HasCurrentHeader(This,hasCurrent) \ + ( (This)->lpVtbl -> get_HasCurrentHeader(This,hasCurrent) ) + +#define ICoreWebView2HttpHeadersCollectionIterator_MoveNext(This,hasNext) \ + ( (This)->lpVtbl -> MoveNext(This,hasNext) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2HttpHeadersCollectionIterator_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2HttpRequestHeaders_INTERFACE_DEFINED__ +#define __ICoreWebView2HttpRequestHeaders_INTERFACE_DEFINED__ + +/* interface ICoreWebView2HttpRequestHeaders */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2HttpRequestHeaders = {0xe86cac0e,0x5523,0x465c,{0xb5,0x36,0x8f,0xb9,0xfc,0x8c,0x8c,0x60}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("e86cac0e-5523-465c-b536-8fb9fc8c8c60") + ICoreWebView2HttpRequestHeaders : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetHeader( + /* [in] */ LPCWSTR name, + /* [retval][out] */ LPWSTR *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetHeaders( + /* [in] */ LPCWSTR name, + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator) = 0; + + virtual HRESULT STDMETHODCALLTYPE Contains( + /* [in] */ LPCWSTR name, + /* [retval][out] */ BOOL *contains) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetHeader( + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR value) = 0; + + virtual HRESULT STDMETHODCALLTYPE RemoveHeader( + /* [in] */ LPCWSTR name) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetIterator( + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2HttpRequestHeadersVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2HttpRequestHeaders * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2HttpRequestHeaders * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2HttpRequestHeaders * This); + + HRESULT ( STDMETHODCALLTYPE *GetHeader )( + ICoreWebView2HttpRequestHeaders * This, + /* [in] */ LPCWSTR name, + /* [retval][out] */ LPWSTR *value); + + HRESULT ( STDMETHODCALLTYPE *GetHeaders )( + ICoreWebView2HttpRequestHeaders * This, + /* [in] */ LPCWSTR name, + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator); + + HRESULT ( STDMETHODCALLTYPE *Contains )( + ICoreWebView2HttpRequestHeaders * This, + /* [in] */ LPCWSTR name, + /* [retval][out] */ BOOL *contains); + + HRESULT ( STDMETHODCALLTYPE *SetHeader )( + ICoreWebView2HttpRequestHeaders * This, + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR value); + + HRESULT ( STDMETHODCALLTYPE *RemoveHeader )( + ICoreWebView2HttpRequestHeaders * This, + /* [in] */ LPCWSTR name); + + HRESULT ( STDMETHODCALLTYPE *GetIterator )( + ICoreWebView2HttpRequestHeaders * This, + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator); + + END_INTERFACE + } ICoreWebView2HttpRequestHeadersVtbl; + + interface ICoreWebView2HttpRequestHeaders + { + CONST_VTBL struct ICoreWebView2HttpRequestHeadersVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2HttpRequestHeaders_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2HttpRequestHeaders_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2HttpRequestHeaders_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2HttpRequestHeaders_GetHeader(This,name,value) \ + ( (This)->lpVtbl -> GetHeader(This,name,value) ) + +#define ICoreWebView2HttpRequestHeaders_GetHeaders(This,name,iterator) \ + ( (This)->lpVtbl -> GetHeaders(This,name,iterator) ) + +#define ICoreWebView2HttpRequestHeaders_Contains(This,name,contains) \ + ( (This)->lpVtbl -> Contains(This,name,contains) ) + +#define ICoreWebView2HttpRequestHeaders_SetHeader(This,name,value) \ + ( (This)->lpVtbl -> SetHeader(This,name,value) ) + +#define ICoreWebView2HttpRequestHeaders_RemoveHeader(This,name) \ + ( (This)->lpVtbl -> RemoveHeader(This,name) ) + +#define ICoreWebView2HttpRequestHeaders_GetIterator(This,iterator) \ + ( (This)->lpVtbl -> GetIterator(This,iterator) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2HttpRequestHeaders_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2HttpResponseHeaders_INTERFACE_DEFINED__ +#define __ICoreWebView2HttpResponseHeaders_INTERFACE_DEFINED__ + +/* interface ICoreWebView2HttpResponseHeaders */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2HttpResponseHeaders = {0x03c5ff5a,0x9b45,0x4a88,{0x88,0x1c,0x89,0xa9,0xf3,0x28,0x61,0x9c}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("03c5ff5a-9b45-4a88-881c-89a9f328619c") + ICoreWebView2HttpResponseHeaders : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AppendHeader( + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR value) = 0; + + virtual HRESULT STDMETHODCALLTYPE Contains( + /* [in] */ LPCWSTR name, + /* [retval][out] */ BOOL *contains) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetHeader( + /* [in] */ LPCWSTR name, + /* [retval][out] */ LPWSTR *value) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetHeaders( + /* [in] */ LPCWSTR name, + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetIterator( + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2HttpResponseHeadersVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2HttpResponseHeaders * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2HttpResponseHeaders * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2HttpResponseHeaders * This); + + HRESULT ( STDMETHODCALLTYPE *AppendHeader )( + ICoreWebView2HttpResponseHeaders * This, + /* [in] */ LPCWSTR name, + /* [in] */ LPCWSTR value); + + HRESULT ( STDMETHODCALLTYPE *Contains )( + ICoreWebView2HttpResponseHeaders * This, + /* [in] */ LPCWSTR name, + /* [retval][out] */ BOOL *contains); + + HRESULT ( STDMETHODCALLTYPE *GetHeader )( + ICoreWebView2HttpResponseHeaders * This, + /* [in] */ LPCWSTR name, + /* [retval][out] */ LPWSTR *value); + + HRESULT ( STDMETHODCALLTYPE *GetHeaders )( + ICoreWebView2HttpResponseHeaders * This, + /* [in] */ LPCWSTR name, + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator); + + HRESULT ( STDMETHODCALLTYPE *GetIterator )( + ICoreWebView2HttpResponseHeaders * This, + /* [retval][out] */ ICoreWebView2HttpHeadersCollectionIterator **iterator); + + END_INTERFACE + } ICoreWebView2HttpResponseHeadersVtbl; + + interface ICoreWebView2HttpResponseHeaders + { + CONST_VTBL struct ICoreWebView2HttpResponseHeadersVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2HttpResponseHeaders_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2HttpResponseHeaders_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2HttpResponseHeaders_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2HttpResponseHeaders_AppendHeader(This,name,value) \ + ( (This)->lpVtbl -> AppendHeader(This,name,value) ) + +#define ICoreWebView2HttpResponseHeaders_Contains(This,name,contains) \ + ( (This)->lpVtbl -> Contains(This,name,contains) ) + +#define ICoreWebView2HttpResponseHeaders_GetHeader(This,name,value) \ + ( (This)->lpVtbl -> GetHeader(This,name,value) ) + +#define ICoreWebView2HttpResponseHeaders_GetHeaders(This,name,iterator) \ + ( (This)->lpVtbl -> GetHeaders(This,name,iterator) ) + +#define ICoreWebView2HttpResponseHeaders_GetIterator(This,iterator) \ + ( (This)->lpVtbl -> GetIterator(This,iterator) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2HttpResponseHeaders_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Interop_INTERFACE_DEFINED__ +#define __ICoreWebView2Interop_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Interop */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Interop = {0x912b34a7,0xd10b,0x49c4,{0xaf,0x18,0x7c,0xb7,0xe6,0x04,0xe0,0x1a}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("912b34a7-d10b-49c4-af18-7cb7e604e01a") + ICoreWebView2Interop : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AddHostObjectToScript( + /* [in] */ LPCWSTR name, + /* [in] */ VARIANT *object) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2InteropVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Interop * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Interop * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Interop * This); + + HRESULT ( STDMETHODCALLTYPE *AddHostObjectToScript )( + ICoreWebView2Interop * This, + /* [in] */ LPCWSTR name, + /* [in] */ VARIANT *object); + + END_INTERFACE + } ICoreWebView2InteropVtbl; + + interface ICoreWebView2Interop + { + CONST_VTBL struct ICoreWebView2InteropVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Interop_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Interop_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Interop_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Interop_AddHostObjectToScript(This,name,object) \ + ( (This)->lpVtbl -> AddHostObjectToScript(This,name,object) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Interop_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2MoveFocusRequestedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2MoveFocusRequestedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2MoveFocusRequestedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2MoveFocusRequestedEventArgs = {0x2d6aa13b,0x3839,0x4a15,{0x92,0xfc,0xd8,0x8b,0x3c,0x0d,0x9c,0x9d}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("2d6aa13b-3839-4a15-92fc-d88b3c0d9c9d") + ICoreWebView2MoveFocusRequestedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Reason( + /* [retval][out] */ COREWEBVIEW2_MOVE_FOCUS_REASON *reason) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Handled( + /* [retval][out] */ BOOL *value) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Handled( + /* [in] */ BOOL value) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2MoveFocusRequestedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2MoveFocusRequestedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2MoveFocusRequestedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2MoveFocusRequestedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Reason )( + ICoreWebView2MoveFocusRequestedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_MOVE_FOCUS_REASON *reason); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Handled )( + ICoreWebView2MoveFocusRequestedEventArgs * This, + /* [retval][out] */ BOOL *value); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Handled )( + ICoreWebView2MoveFocusRequestedEventArgs * This, + /* [in] */ BOOL value); + + END_INTERFACE + } ICoreWebView2MoveFocusRequestedEventArgsVtbl; + + interface ICoreWebView2MoveFocusRequestedEventArgs + { + CONST_VTBL struct ICoreWebView2MoveFocusRequestedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2MoveFocusRequestedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2MoveFocusRequestedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2MoveFocusRequestedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2MoveFocusRequestedEventArgs_get_Reason(This,reason) \ + ( (This)->lpVtbl -> get_Reason(This,reason) ) + +#define ICoreWebView2MoveFocusRequestedEventArgs_get_Handled(This,value) \ + ( (This)->lpVtbl -> get_Handled(This,value) ) + +#define ICoreWebView2MoveFocusRequestedEventArgs_put_Handled(This,value) \ + ( (This)->lpVtbl -> put_Handled(This,value) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2MoveFocusRequestedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2MoveFocusRequestedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2MoveFocusRequestedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2MoveFocusRequestedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2MoveFocusRequestedEventHandler = {0x69035451,0x6dc7,0x4cb8,{0x9b,0xce,0xb2,0xbd,0x70,0xad,0x28,0x9f}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("69035451-6dc7-4cb8-9bce-b2bd70ad289f") + ICoreWebView2MoveFocusRequestedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ ICoreWebView2MoveFocusRequestedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2MoveFocusRequestedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2MoveFocusRequestedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2MoveFocusRequestedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2MoveFocusRequestedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2MoveFocusRequestedEventHandler * This, + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ ICoreWebView2MoveFocusRequestedEventArgs *args); + + END_INTERFACE + } ICoreWebView2MoveFocusRequestedEventHandlerVtbl; + + interface ICoreWebView2MoveFocusRequestedEventHandler + { + CONST_VTBL struct ICoreWebView2MoveFocusRequestedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2MoveFocusRequestedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2MoveFocusRequestedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2MoveFocusRequestedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2MoveFocusRequestedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2MoveFocusRequestedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationCompletedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2NavigationCompletedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2NavigationCompletedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2NavigationCompletedEventArgs = {0x30d68b7d,0x20d9,0x4752,{0xa9,0xca,0xec,0x84,0x48,0xfb,0xb5,0xc1}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("30d68b7d-20d9-4752-a9ca-ec8448fbb5c1") + ICoreWebView2NavigationCompletedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsSuccess( + /* [retval][out] */ BOOL *isSuccess) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_WebErrorStatus( + /* [retval][out] */ COREWEBVIEW2_WEB_ERROR_STATUS *webErrorStatus) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_NavigationId( + /* [retval][out] */ UINT64 *navigationId) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2NavigationCompletedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2NavigationCompletedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2NavigationCompletedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2NavigationCompletedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsSuccess )( + ICoreWebView2NavigationCompletedEventArgs * This, + /* [retval][out] */ BOOL *isSuccess); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_WebErrorStatus )( + ICoreWebView2NavigationCompletedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_WEB_ERROR_STATUS *webErrorStatus); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_NavigationId )( + ICoreWebView2NavigationCompletedEventArgs * This, + /* [retval][out] */ UINT64 *navigationId); + + END_INTERFACE + } ICoreWebView2NavigationCompletedEventArgsVtbl; + + interface ICoreWebView2NavigationCompletedEventArgs + { + CONST_VTBL struct ICoreWebView2NavigationCompletedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2NavigationCompletedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2NavigationCompletedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2NavigationCompletedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2NavigationCompletedEventArgs_get_IsSuccess(This,isSuccess) \ + ( (This)->lpVtbl -> get_IsSuccess(This,isSuccess) ) + +#define ICoreWebView2NavigationCompletedEventArgs_get_WebErrorStatus(This,webErrorStatus) \ + ( (This)->lpVtbl -> get_WebErrorStatus(This,webErrorStatus) ) + +#define ICoreWebView2NavigationCompletedEventArgs_get_NavigationId(This,navigationId) \ + ( (This)->lpVtbl -> get_NavigationId(This,navigationId) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2NavigationCompletedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationCompletedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2NavigationCompletedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2NavigationCompletedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2NavigationCompletedEventHandler = {0xd33a35bf,0x1c49,0x4f98,{0x93,0xab,0x00,0x6e,0x05,0x33,0xfe,0x1c}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("d33a35bf-1c49-4f98-93ab-006e0533fe1c") + ICoreWebView2NavigationCompletedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2NavigationCompletedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2NavigationCompletedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2NavigationCompletedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2NavigationCompletedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2NavigationCompletedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2NavigationCompletedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2NavigationCompletedEventArgs *args); + + END_INTERFACE + } ICoreWebView2NavigationCompletedEventHandlerVtbl; + + interface ICoreWebView2NavigationCompletedEventHandler + { + CONST_VTBL struct ICoreWebView2NavigationCompletedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2NavigationCompletedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2NavigationCompletedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2NavigationCompletedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2NavigationCompletedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2NavigationCompletedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationStartingEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2NavigationStartingEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2NavigationStartingEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2NavigationStartingEventArgs = {0x5b495469,0xe119,0x438a,{0x9b,0x18,0x76,0x04,0xf2,0x5f,0x2e,0x49}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("5b495469-e119-438a-9b18-7604f25f2e49") + ICoreWebView2NavigationStartingEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Uri( + /* [retval][out] */ LPWSTR *uri) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsUserInitiated( + /* [retval][out] */ BOOL *isUserInitiated) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsRedirected( + /* [retval][out] */ BOOL *isRedirected) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RequestHeaders( + /* [retval][out] */ ICoreWebView2HttpRequestHeaders **requestHeaders) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Cancel( + /* [retval][out] */ BOOL *cancel) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Cancel( + /* [in] */ BOOL cancel) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_NavigationId( + /* [retval][out] */ UINT64 *navigationId) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2NavigationStartingEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2NavigationStartingEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2NavigationStartingEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Uri )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [retval][out] */ LPWSTR *uri); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsUserInitiated )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [retval][out] */ BOOL *isUserInitiated); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsRedirected )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [retval][out] */ BOOL *isRedirected); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_RequestHeaders )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [retval][out] */ ICoreWebView2HttpRequestHeaders **requestHeaders); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Cancel )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [retval][out] */ BOOL *cancel); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Cancel )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [in] */ BOOL cancel); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_NavigationId )( + ICoreWebView2NavigationStartingEventArgs * This, + /* [retval][out] */ UINT64 *navigationId); + + END_INTERFACE + } ICoreWebView2NavigationStartingEventArgsVtbl; + + interface ICoreWebView2NavigationStartingEventArgs + { + CONST_VTBL struct ICoreWebView2NavigationStartingEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2NavigationStartingEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2NavigationStartingEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2NavigationStartingEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2NavigationStartingEventArgs_get_Uri(This,uri) \ + ( (This)->lpVtbl -> get_Uri(This,uri) ) + +#define ICoreWebView2NavigationStartingEventArgs_get_IsUserInitiated(This,isUserInitiated) \ + ( (This)->lpVtbl -> get_IsUserInitiated(This,isUserInitiated) ) + +#define ICoreWebView2NavigationStartingEventArgs_get_IsRedirected(This,isRedirected) \ + ( (This)->lpVtbl -> get_IsRedirected(This,isRedirected) ) + +#define ICoreWebView2NavigationStartingEventArgs_get_RequestHeaders(This,requestHeaders) \ + ( (This)->lpVtbl -> get_RequestHeaders(This,requestHeaders) ) + +#define ICoreWebView2NavigationStartingEventArgs_get_Cancel(This,cancel) \ + ( (This)->lpVtbl -> get_Cancel(This,cancel) ) + +#define ICoreWebView2NavigationStartingEventArgs_put_Cancel(This,cancel) \ + ( (This)->lpVtbl -> put_Cancel(This,cancel) ) + +#define ICoreWebView2NavigationStartingEventArgs_get_NavigationId(This,navigationId) \ + ( (This)->lpVtbl -> get_NavigationId(This,navigationId) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2NavigationStartingEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2NavigationStartingEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2NavigationStartingEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2NavigationStartingEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2NavigationStartingEventHandler = {0x9adbe429,0xf36d,0x432b,{0x9d,0xdc,0xf8,0x88,0x1f,0xbd,0x76,0xe3}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9adbe429-f36d-432b-9ddc-f8881fbd76e3") + ICoreWebView2NavigationStartingEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2NavigationStartingEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2NavigationStartingEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2NavigationStartingEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2NavigationStartingEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2NavigationStartingEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2NavigationStartingEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2NavigationStartingEventArgs *args); + + END_INTERFACE + } ICoreWebView2NavigationStartingEventHandlerVtbl; + + interface ICoreWebView2NavigationStartingEventHandler + { + CONST_VTBL struct ICoreWebView2NavigationStartingEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2NavigationStartingEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2NavigationStartingEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2NavigationStartingEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2NavigationStartingEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2NavigationStartingEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2NewBrowserVersionAvailableEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2NewBrowserVersionAvailableEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2NewBrowserVersionAvailableEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2NewBrowserVersionAvailableEventHandler = {0xf9a2976e,0xd34e,0x44fc,{0xad,0xee,0x81,0xb6,0xb5,0x7c,0xa9,0x14}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("f9a2976e-d34e-44fc-adee-81b6b57ca914") + ICoreWebView2NewBrowserVersionAvailableEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2Environment *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2NewBrowserVersionAvailableEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2NewBrowserVersionAvailableEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2NewBrowserVersionAvailableEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2NewBrowserVersionAvailableEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2NewBrowserVersionAvailableEventHandler * This, + /* [in] */ ICoreWebView2Environment *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2NewBrowserVersionAvailableEventHandlerVtbl; + + interface ICoreWebView2NewBrowserVersionAvailableEventHandler + { + CONST_VTBL struct ICoreWebView2NewBrowserVersionAvailableEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2NewBrowserVersionAvailableEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2NewBrowserVersionAvailableEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2NewBrowserVersionAvailableEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2NewBrowserVersionAvailableEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2NewBrowserVersionAvailableEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2NewWindowRequestedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2NewWindowRequestedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2NewWindowRequestedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2NewWindowRequestedEventArgs = {0x34acb11c,0xfc37,0x4418,{0x91,0x32,0xf9,0xc2,0x1d,0x1e,0xaf,0xb9}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("34acb11c-fc37-4418-9132-f9c21d1eafb9") + ICoreWebView2NewWindowRequestedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Uri( + /* [retval][out] */ LPWSTR *uri) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_NewWindow( + /* [in] */ ICoreWebView2 *newWindow) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_NewWindow( + /* [retval][out] */ ICoreWebView2 **newWindow) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Handled( + /* [in] */ BOOL handled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Handled( + /* [retval][out] */ BOOL *handled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsUserInitiated( + /* [retval][out] */ BOOL *isUserInitiated) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDeferral( + /* [retval][out] */ ICoreWebView2Deferral **deferral) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_WindowFeatures( + /* [retval][out] */ ICoreWebView2WindowFeatures **value) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2NewWindowRequestedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2NewWindowRequestedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2NewWindowRequestedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Uri )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [retval][out] */ LPWSTR *uri); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_NewWindow )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [in] */ ICoreWebView2 *newWindow); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_NewWindow )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [retval][out] */ ICoreWebView2 **newWindow); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Handled )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [in] */ BOOL handled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Handled )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [retval][out] */ BOOL *handled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsUserInitiated )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [retval][out] */ BOOL *isUserInitiated); + + HRESULT ( STDMETHODCALLTYPE *GetDeferral )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [retval][out] */ ICoreWebView2Deferral **deferral); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_WindowFeatures )( + ICoreWebView2NewWindowRequestedEventArgs * This, + /* [retval][out] */ ICoreWebView2WindowFeatures **value); + + END_INTERFACE + } ICoreWebView2NewWindowRequestedEventArgsVtbl; + + interface ICoreWebView2NewWindowRequestedEventArgs + { + CONST_VTBL struct ICoreWebView2NewWindowRequestedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2NewWindowRequestedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2NewWindowRequestedEventArgs_get_Uri(This,uri) \ + ( (This)->lpVtbl -> get_Uri(This,uri) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_put_NewWindow(This,newWindow) \ + ( (This)->lpVtbl -> put_NewWindow(This,newWindow) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_get_NewWindow(This,newWindow) \ + ( (This)->lpVtbl -> get_NewWindow(This,newWindow) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_put_Handled(This,handled) \ + ( (This)->lpVtbl -> put_Handled(This,handled) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_get_Handled(This,handled) \ + ( (This)->lpVtbl -> get_Handled(This,handled) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_get_IsUserInitiated(This,isUserInitiated) \ + ( (This)->lpVtbl -> get_IsUserInitiated(This,isUserInitiated) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_GetDeferral(This,deferral) \ + ( (This)->lpVtbl -> GetDeferral(This,deferral) ) + +#define ICoreWebView2NewWindowRequestedEventArgs_get_WindowFeatures(This,value) \ + ( (This)->lpVtbl -> get_WindowFeatures(This,value) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2NewWindowRequestedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2NewWindowRequestedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2NewWindowRequestedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2NewWindowRequestedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2NewWindowRequestedEventHandler = {0xd4c185fe,0xc81c,0x4989,{0x97,0xaf,0x2d,0x3f,0xa7,0xab,0x56,0x51}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("d4c185fe-c81c-4989-97af-2d3fa7ab5651") + ICoreWebView2NewWindowRequestedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2NewWindowRequestedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2NewWindowRequestedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2NewWindowRequestedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2NewWindowRequestedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2NewWindowRequestedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2NewWindowRequestedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2NewWindowRequestedEventArgs *args); + + END_INTERFACE + } ICoreWebView2NewWindowRequestedEventHandlerVtbl; + + interface ICoreWebView2NewWindowRequestedEventHandler + { + CONST_VTBL struct ICoreWebView2NewWindowRequestedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2NewWindowRequestedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2NewWindowRequestedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2NewWindowRequestedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2NewWindowRequestedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2NewWindowRequestedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2PermissionRequestedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2PermissionRequestedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2PermissionRequestedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2PermissionRequestedEventArgs = {0x973ae2ef,0xff18,0x4894,{0x8f,0xb2,0x3c,0x75,0x8f,0x04,0x68,0x10}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("973ae2ef-ff18-4894-8fb2-3c758f046810") + ICoreWebView2PermissionRequestedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Uri( + /* [retval][out] */ LPWSTR *uri) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PermissionKind( + /* [retval][out] */ COREWEBVIEW2_PERMISSION_KIND *permissionKind) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsUserInitiated( + /* [retval][out] */ BOOL *isUserInitiated) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_State( + /* [retval][out] */ COREWEBVIEW2_PERMISSION_STATE *state) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_State( + /* [in] */ COREWEBVIEW2_PERMISSION_STATE state) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDeferral( + /* [retval][out] */ ICoreWebView2Deferral **deferral) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2PermissionRequestedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2PermissionRequestedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2PermissionRequestedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2PermissionRequestedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Uri )( + ICoreWebView2PermissionRequestedEventArgs * This, + /* [retval][out] */ LPWSTR *uri); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PermissionKind )( + ICoreWebView2PermissionRequestedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_PERMISSION_KIND *permissionKind); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsUserInitiated )( + ICoreWebView2PermissionRequestedEventArgs * This, + /* [retval][out] */ BOOL *isUserInitiated); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_State )( + ICoreWebView2PermissionRequestedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_PERMISSION_STATE *state); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_State )( + ICoreWebView2PermissionRequestedEventArgs * This, + /* [in] */ COREWEBVIEW2_PERMISSION_STATE state); + + HRESULT ( STDMETHODCALLTYPE *GetDeferral )( + ICoreWebView2PermissionRequestedEventArgs * This, + /* [retval][out] */ ICoreWebView2Deferral **deferral); + + END_INTERFACE + } ICoreWebView2PermissionRequestedEventArgsVtbl; + + interface ICoreWebView2PermissionRequestedEventArgs + { + CONST_VTBL struct ICoreWebView2PermissionRequestedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2PermissionRequestedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2PermissionRequestedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2PermissionRequestedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2PermissionRequestedEventArgs_get_Uri(This,uri) \ + ( (This)->lpVtbl -> get_Uri(This,uri) ) + +#define ICoreWebView2PermissionRequestedEventArgs_get_PermissionKind(This,permissionKind) \ + ( (This)->lpVtbl -> get_PermissionKind(This,permissionKind) ) + +#define ICoreWebView2PermissionRequestedEventArgs_get_IsUserInitiated(This,isUserInitiated) \ + ( (This)->lpVtbl -> get_IsUserInitiated(This,isUserInitiated) ) + +#define ICoreWebView2PermissionRequestedEventArgs_get_State(This,state) \ + ( (This)->lpVtbl -> get_State(This,state) ) + +#define ICoreWebView2PermissionRequestedEventArgs_put_State(This,state) \ + ( (This)->lpVtbl -> put_State(This,state) ) + +#define ICoreWebView2PermissionRequestedEventArgs_GetDeferral(This,deferral) \ + ( (This)->lpVtbl -> GetDeferral(This,deferral) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2PermissionRequestedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2PermissionRequestedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2PermissionRequestedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2PermissionRequestedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2PermissionRequestedEventHandler = {0x15e1c6a3,0xc72a,0x4df3,{0x91,0xd7,0xd0,0x97,0xfb,0xec,0x6b,0xfd}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("15e1c6a3-c72a-4df3-91d7-d097fbec6bfd") + ICoreWebView2PermissionRequestedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2PermissionRequestedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2PermissionRequestedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2PermissionRequestedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2PermissionRequestedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2PermissionRequestedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2PermissionRequestedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2PermissionRequestedEventArgs *args); + + END_INTERFACE + } ICoreWebView2PermissionRequestedEventHandlerVtbl; + + interface ICoreWebView2PermissionRequestedEventHandler + { + CONST_VTBL struct ICoreWebView2PermissionRequestedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2PermissionRequestedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2PermissionRequestedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2PermissionRequestedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2PermissionRequestedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2PermissionRequestedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2PointerInfo_INTERFACE_DEFINED__ +#define __ICoreWebView2PointerInfo_INTERFACE_DEFINED__ + +/* interface ICoreWebView2PointerInfo */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2PointerInfo = {0xe6995887,0xd10d,0x4f5d,{0x93,0x59,0x4c,0xe4,0x6e,0x4f,0x96,0xb9}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("e6995887-d10d-4f5d-9359-4ce46e4f96b9") + ICoreWebView2PointerInfo : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PointerKind( + /* [retval][out] */ DWORD *pointerKind) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PointerKind( + /* [in] */ DWORD pointerKind) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PointerId( + /* [retval][out] */ UINT32 *pointerId) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PointerId( + /* [in] */ UINT32 pointerId) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_FrameId( + /* [retval][out] */ UINT32 *frameId) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_FrameId( + /* [in] */ UINT32 frameId) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PointerFlags( + /* [retval][out] */ UINT32 *pointerFlags) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PointerFlags( + /* [in] */ UINT32 pointerFlags) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PointerDeviceRect( + /* [retval][out] */ RECT *pointerDeviceRect) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PointerDeviceRect( + /* [in] */ RECT pointerDeviceRect) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_DisplayRect( + /* [retval][out] */ RECT *displayRect) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_DisplayRect( + /* [in] */ RECT displayRect) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PixelLocation( + /* [retval][out] */ POINT *pixelLocation) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PixelLocation( + /* [in] */ POINT pixelLocation) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HimetricLocation( + /* [retval][out] */ POINT *himetricLocation) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_HimetricLocation( + /* [in] */ POINT himetricLocation) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PixelLocationRaw( + /* [retval][out] */ POINT *pixelLocationRaw) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PixelLocationRaw( + /* [in] */ POINT pixelLocationRaw) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HimetricLocationRaw( + /* [retval][out] */ POINT *himetricLocationRaw) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_HimetricLocationRaw( + /* [in] */ POINT himetricLocationRaw) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Time( + /* [retval][out] */ DWORD *time) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Time( + /* [in] */ DWORD time) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HistoryCount( + /* [retval][out] */ UINT32 *historyCount) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_HistoryCount( + /* [in] */ UINT32 historyCount) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_InputData( + /* [retval][out] */ INT32 *inputData) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_InputData( + /* [in] */ INT32 inputData) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_KeyStates( + /* [retval][out] */ DWORD *keyStates) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_KeyStates( + /* [in] */ DWORD keyStates) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PerformanceCount( + /* [retval][out] */ UINT64 *performanceCount) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PerformanceCount( + /* [in] */ UINT64 performanceCount) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ButtonChangeKind( + /* [retval][out] */ INT32 *buttonChangeKind) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_ButtonChangeKind( + /* [in] */ INT32 buttonChangeKind) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PenFlags( + /* [retval][out] */ UINT32 *penFLags) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PenFlags( + /* [in] */ UINT32 penFLags) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PenMask( + /* [retval][out] */ UINT32 *penMask) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PenMask( + /* [in] */ UINT32 penMask) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PenPressure( + /* [retval][out] */ UINT32 *penPressure) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PenPressure( + /* [in] */ UINT32 penPressure) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PenRotation( + /* [retval][out] */ UINT32 *penRotation) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PenRotation( + /* [in] */ UINT32 penRotation) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PenTiltX( + /* [retval][out] */ INT32 *penTiltX) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PenTiltX( + /* [in] */ INT32 penTiltX) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_PenTiltY( + /* [retval][out] */ INT32 *penTiltY) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_PenTiltY( + /* [in] */ INT32 penTiltY) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_TouchFlags( + /* [retval][out] */ UINT32 *touchFlags) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_TouchFlags( + /* [in] */ UINT32 touchFlags) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_TouchMask( + /* [retval][out] */ UINT32 *touchMask) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_TouchMask( + /* [in] */ UINT32 touchMask) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_TouchContact( + /* [retval][out] */ RECT *touchContact) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_TouchContact( + /* [in] */ RECT touchContact) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_TouchContactRaw( + /* [retval][out] */ RECT *touchContactRaw) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_TouchContactRaw( + /* [in] */ RECT touchContactRaw) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_TouchOrientation( + /* [retval][out] */ UINT32 *touchOrientation) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_TouchOrientation( + /* [in] */ UINT32 touchOrientation) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_TouchPressure( + /* [retval][out] */ UINT32 *touchPressure) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_TouchPressure( + /* [in] */ UINT32 touchPressure) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2PointerInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2PointerInfo * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2PointerInfo * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2PointerInfo * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PointerKind )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ DWORD *pointerKind); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PointerKind )( + ICoreWebView2PointerInfo * This, + /* [in] */ DWORD pointerKind); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PointerId )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *pointerId); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PointerId )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 pointerId); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_FrameId )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *frameId); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_FrameId )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 frameId); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PointerFlags )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *pointerFlags); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PointerFlags )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 pointerFlags); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PointerDeviceRect )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ RECT *pointerDeviceRect); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PointerDeviceRect )( + ICoreWebView2PointerInfo * This, + /* [in] */ RECT pointerDeviceRect); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DisplayRect )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ RECT *displayRect); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_DisplayRect )( + ICoreWebView2PointerInfo * This, + /* [in] */ RECT displayRect); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PixelLocation )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ POINT *pixelLocation); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PixelLocation )( + ICoreWebView2PointerInfo * This, + /* [in] */ POINT pixelLocation); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HimetricLocation )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ POINT *himetricLocation); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_HimetricLocation )( + ICoreWebView2PointerInfo * This, + /* [in] */ POINT himetricLocation); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PixelLocationRaw )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ POINT *pixelLocationRaw); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PixelLocationRaw )( + ICoreWebView2PointerInfo * This, + /* [in] */ POINT pixelLocationRaw); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HimetricLocationRaw )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ POINT *himetricLocationRaw); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_HimetricLocationRaw )( + ICoreWebView2PointerInfo * This, + /* [in] */ POINT himetricLocationRaw); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Time )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ DWORD *time); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Time )( + ICoreWebView2PointerInfo * This, + /* [in] */ DWORD time); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HistoryCount )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *historyCount); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_HistoryCount )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 historyCount); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_InputData )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ INT32 *inputData); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_InputData )( + ICoreWebView2PointerInfo * This, + /* [in] */ INT32 inputData); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_KeyStates )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ DWORD *keyStates); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_KeyStates )( + ICoreWebView2PointerInfo * This, + /* [in] */ DWORD keyStates); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PerformanceCount )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT64 *performanceCount); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PerformanceCount )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT64 performanceCount); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ButtonChangeKind )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ INT32 *buttonChangeKind); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ButtonChangeKind )( + ICoreWebView2PointerInfo * This, + /* [in] */ INT32 buttonChangeKind); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PenFlags )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *penFLags); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PenFlags )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 penFLags); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PenMask )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *penMask); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PenMask )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 penMask); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PenPressure )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *penPressure); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PenPressure )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 penPressure); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PenRotation )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *penRotation); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PenRotation )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 penRotation); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PenTiltX )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ INT32 *penTiltX); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PenTiltX )( + ICoreWebView2PointerInfo * This, + /* [in] */ INT32 penTiltX); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_PenTiltY )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ INT32 *penTiltY); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_PenTiltY )( + ICoreWebView2PointerInfo * This, + /* [in] */ INT32 penTiltY); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_TouchFlags )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *touchFlags); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_TouchFlags )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 touchFlags); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_TouchMask )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *touchMask); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_TouchMask )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 touchMask); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_TouchContact )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ RECT *touchContact); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_TouchContact )( + ICoreWebView2PointerInfo * This, + /* [in] */ RECT touchContact); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_TouchContactRaw )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ RECT *touchContactRaw); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_TouchContactRaw )( + ICoreWebView2PointerInfo * This, + /* [in] */ RECT touchContactRaw); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_TouchOrientation )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *touchOrientation); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_TouchOrientation )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 touchOrientation); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_TouchPressure )( + ICoreWebView2PointerInfo * This, + /* [retval][out] */ UINT32 *touchPressure); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_TouchPressure )( + ICoreWebView2PointerInfo * This, + /* [in] */ UINT32 touchPressure); + + END_INTERFACE + } ICoreWebView2PointerInfoVtbl; + + interface ICoreWebView2PointerInfo + { + CONST_VTBL struct ICoreWebView2PointerInfoVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2PointerInfo_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2PointerInfo_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2PointerInfo_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2PointerInfo_get_PointerKind(This,pointerKind) \ + ( (This)->lpVtbl -> get_PointerKind(This,pointerKind) ) + +#define ICoreWebView2PointerInfo_put_PointerKind(This,pointerKind) \ + ( (This)->lpVtbl -> put_PointerKind(This,pointerKind) ) + +#define ICoreWebView2PointerInfo_get_PointerId(This,pointerId) \ + ( (This)->lpVtbl -> get_PointerId(This,pointerId) ) + +#define ICoreWebView2PointerInfo_put_PointerId(This,pointerId) \ + ( (This)->lpVtbl -> put_PointerId(This,pointerId) ) + +#define ICoreWebView2PointerInfo_get_FrameId(This,frameId) \ + ( (This)->lpVtbl -> get_FrameId(This,frameId) ) + +#define ICoreWebView2PointerInfo_put_FrameId(This,frameId) \ + ( (This)->lpVtbl -> put_FrameId(This,frameId) ) + +#define ICoreWebView2PointerInfo_get_PointerFlags(This,pointerFlags) \ + ( (This)->lpVtbl -> get_PointerFlags(This,pointerFlags) ) + +#define ICoreWebView2PointerInfo_put_PointerFlags(This,pointerFlags) \ + ( (This)->lpVtbl -> put_PointerFlags(This,pointerFlags) ) + +#define ICoreWebView2PointerInfo_get_PointerDeviceRect(This,pointerDeviceRect) \ + ( (This)->lpVtbl -> get_PointerDeviceRect(This,pointerDeviceRect) ) + +#define ICoreWebView2PointerInfo_put_PointerDeviceRect(This,pointerDeviceRect) \ + ( (This)->lpVtbl -> put_PointerDeviceRect(This,pointerDeviceRect) ) + +#define ICoreWebView2PointerInfo_get_DisplayRect(This,displayRect) \ + ( (This)->lpVtbl -> get_DisplayRect(This,displayRect) ) + +#define ICoreWebView2PointerInfo_put_DisplayRect(This,displayRect) \ + ( (This)->lpVtbl -> put_DisplayRect(This,displayRect) ) + +#define ICoreWebView2PointerInfo_get_PixelLocation(This,pixelLocation) \ + ( (This)->lpVtbl -> get_PixelLocation(This,pixelLocation) ) + +#define ICoreWebView2PointerInfo_put_PixelLocation(This,pixelLocation) \ + ( (This)->lpVtbl -> put_PixelLocation(This,pixelLocation) ) + +#define ICoreWebView2PointerInfo_get_HimetricLocation(This,himetricLocation) \ + ( (This)->lpVtbl -> get_HimetricLocation(This,himetricLocation) ) + +#define ICoreWebView2PointerInfo_put_HimetricLocation(This,himetricLocation) \ + ( (This)->lpVtbl -> put_HimetricLocation(This,himetricLocation) ) + +#define ICoreWebView2PointerInfo_get_PixelLocationRaw(This,pixelLocationRaw) \ + ( (This)->lpVtbl -> get_PixelLocationRaw(This,pixelLocationRaw) ) + +#define ICoreWebView2PointerInfo_put_PixelLocationRaw(This,pixelLocationRaw) \ + ( (This)->lpVtbl -> put_PixelLocationRaw(This,pixelLocationRaw) ) + +#define ICoreWebView2PointerInfo_get_HimetricLocationRaw(This,himetricLocationRaw) \ + ( (This)->lpVtbl -> get_HimetricLocationRaw(This,himetricLocationRaw) ) + +#define ICoreWebView2PointerInfo_put_HimetricLocationRaw(This,himetricLocationRaw) \ + ( (This)->lpVtbl -> put_HimetricLocationRaw(This,himetricLocationRaw) ) + +#define ICoreWebView2PointerInfo_get_Time(This,time) \ + ( (This)->lpVtbl -> get_Time(This,time) ) + +#define ICoreWebView2PointerInfo_put_Time(This,time) \ + ( (This)->lpVtbl -> put_Time(This,time) ) + +#define ICoreWebView2PointerInfo_get_HistoryCount(This,historyCount) \ + ( (This)->lpVtbl -> get_HistoryCount(This,historyCount) ) + +#define ICoreWebView2PointerInfo_put_HistoryCount(This,historyCount) \ + ( (This)->lpVtbl -> put_HistoryCount(This,historyCount) ) + +#define ICoreWebView2PointerInfo_get_InputData(This,inputData) \ + ( (This)->lpVtbl -> get_InputData(This,inputData) ) + +#define ICoreWebView2PointerInfo_put_InputData(This,inputData) \ + ( (This)->lpVtbl -> put_InputData(This,inputData) ) + +#define ICoreWebView2PointerInfo_get_KeyStates(This,keyStates) \ + ( (This)->lpVtbl -> get_KeyStates(This,keyStates) ) + +#define ICoreWebView2PointerInfo_put_KeyStates(This,keyStates) \ + ( (This)->lpVtbl -> put_KeyStates(This,keyStates) ) + +#define ICoreWebView2PointerInfo_get_PerformanceCount(This,performanceCount) \ + ( (This)->lpVtbl -> get_PerformanceCount(This,performanceCount) ) + +#define ICoreWebView2PointerInfo_put_PerformanceCount(This,performanceCount) \ + ( (This)->lpVtbl -> put_PerformanceCount(This,performanceCount) ) + +#define ICoreWebView2PointerInfo_get_ButtonChangeKind(This,buttonChangeKind) \ + ( (This)->lpVtbl -> get_ButtonChangeKind(This,buttonChangeKind) ) + +#define ICoreWebView2PointerInfo_put_ButtonChangeKind(This,buttonChangeKind) \ + ( (This)->lpVtbl -> put_ButtonChangeKind(This,buttonChangeKind) ) + +#define ICoreWebView2PointerInfo_get_PenFlags(This,penFLags) \ + ( (This)->lpVtbl -> get_PenFlags(This,penFLags) ) + +#define ICoreWebView2PointerInfo_put_PenFlags(This,penFLags) \ + ( (This)->lpVtbl -> put_PenFlags(This,penFLags) ) + +#define ICoreWebView2PointerInfo_get_PenMask(This,penMask) \ + ( (This)->lpVtbl -> get_PenMask(This,penMask) ) + +#define ICoreWebView2PointerInfo_put_PenMask(This,penMask) \ + ( (This)->lpVtbl -> put_PenMask(This,penMask) ) + +#define ICoreWebView2PointerInfo_get_PenPressure(This,penPressure) \ + ( (This)->lpVtbl -> get_PenPressure(This,penPressure) ) + +#define ICoreWebView2PointerInfo_put_PenPressure(This,penPressure) \ + ( (This)->lpVtbl -> put_PenPressure(This,penPressure) ) + +#define ICoreWebView2PointerInfo_get_PenRotation(This,penRotation) \ + ( (This)->lpVtbl -> get_PenRotation(This,penRotation) ) + +#define ICoreWebView2PointerInfo_put_PenRotation(This,penRotation) \ + ( (This)->lpVtbl -> put_PenRotation(This,penRotation) ) + +#define ICoreWebView2PointerInfo_get_PenTiltX(This,penTiltX) \ + ( (This)->lpVtbl -> get_PenTiltX(This,penTiltX) ) + +#define ICoreWebView2PointerInfo_put_PenTiltX(This,penTiltX) \ + ( (This)->lpVtbl -> put_PenTiltX(This,penTiltX) ) + +#define ICoreWebView2PointerInfo_get_PenTiltY(This,penTiltY) \ + ( (This)->lpVtbl -> get_PenTiltY(This,penTiltY) ) + +#define ICoreWebView2PointerInfo_put_PenTiltY(This,penTiltY) \ + ( (This)->lpVtbl -> put_PenTiltY(This,penTiltY) ) + +#define ICoreWebView2PointerInfo_get_TouchFlags(This,touchFlags) \ + ( (This)->lpVtbl -> get_TouchFlags(This,touchFlags) ) + +#define ICoreWebView2PointerInfo_put_TouchFlags(This,touchFlags) \ + ( (This)->lpVtbl -> put_TouchFlags(This,touchFlags) ) + +#define ICoreWebView2PointerInfo_get_TouchMask(This,touchMask) \ + ( (This)->lpVtbl -> get_TouchMask(This,touchMask) ) + +#define ICoreWebView2PointerInfo_put_TouchMask(This,touchMask) \ + ( (This)->lpVtbl -> put_TouchMask(This,touchMask) ) + +#define ICoreWebView2PointerInfo_get_TouchContact(This,touchContact) \ + ( (This)->lpVtbl -> get_TouchContact(This,touchContact) ) + +#define ICoreWebView2PointerInfo_put_TouchContact(This,touchContact) \ + ( (This)->lpVtbl -> put_TouchContact(This,touchContact) ) + +#define ICoreWebView2PointerInfo_get_TouchContactRaw(This,touchContactRaw) \ + ( (This)->lpVtbl -> get_TouchContactRaw(This,touchContactRaw) ) + +#define ICoreWebView2PointerInfo_put_TouchContactRaw(This,touchContactRaw) \ + ( (This)->lpVtbl -> put_TouchContactRaw(This,touchContactRaw) ) + +#define ICoreWebView2PointerInfo_get_TouchOrientation(This,touchOrientation) \ + ( (This)->lpVtbl -> get_TouchOrientation(This,touchOrientation) ) + +#define ICoreWebView2PointerInfo_put_TouchOrientation(This,touchOrientation) \ + ( (This)->lpVtbl -> put_TouchOrientation(This,touchOrientation) ) + +#define ICoreWebView2PointerInfo_get_TouchPressure(This,touchPressure) \ + ( (This)->lpVtbl -> get_TouchPressure(This,touchPressure) ) + +#define ICoreWebView2PointerInfo_put_TouchPressure(This,touchPressure) \ + ( (This)->lpVtbl -> put_TouchPressure(This,touchPressure) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2PointerInfo_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ProcessFailedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2ProcessFailedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ProcessFailedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ProcessFailedEventArgs = {0x8155a9a4,0x1474,0x4a86,{0x8c,0xae,0x15,0x1b,0x0f,0xa6,0xb8,0xca}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("8155a9a4-1474-4a86-8cae-151b0fa6b8ca") + ICoreWebView2ProcessFailedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ProcessFailedKind( + /* [retval][out] */ COREWEBVIEW2_PROCESS_FAILED_KIND *processFailedKind) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ProcessFailedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ProcessFailedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ProcessFailedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ProcessFailedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ProcessFailedKind )( + ICoreWebView2ProcessFailedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_PROCESS_FAILED_KIND *processFailedKind); + + END_INTERFACE + } ICoreWebView2ProcessFailedEventArgsVtbl; + + interface ICoreWebView2ProcessFailedEventArgs + { + CONST_VTBL struct ICoreWebView2ProcessFailedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ProcessFailedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ProcessFailedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ProcessFailedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ProcessFailedEventArgs_get_ProcessFailedKind(This,processFailedKind) \ + ( (This)->lpVtbl -> get_ProcessFailedKind(This,processFailedKind) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ProcessFailedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ProcessFailedEventArgs2_INTERFACE_DEFINED__ +#define __ICoreWebView2ProcessFailedEventArgs2_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ProcessFailedEventArgs2 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ProcessFailedEventArgs2 = {0x4dab9422,0x46fa,0x4c3e,{0xa5,0xd2,0x41,0xd2,0x07,0x1d,0x36,0x80}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("4dab9422-46fa-4c3e-a5d2-41d2071d3680") + ICoreWebView2ProcessFailedEventArgs2 : public ICoreWebView2ProcessFailedEventArgs + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Reason( + /* [retval][out] */ COREWEBVIEW2_PROCESS_FAILED_REASON *reason) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ExitCode( + /* [retval][out] */ int *exitCode) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ProcessDescription( + /* [retval][out] */ LPWSTR *processDescription) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_FrameInfosForFailedProcess( + /* [retval][out] */ ICoreWebView2FrameInfoCollection **frames) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ProcessFailedEventArgs2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ProcessFailedEventArgs2 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ProcessFailedEventArgs2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ProcessFailedEventArgs2 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ProcessFailedKind )( + ICoreWebView2ProcessFailedEventArgs2 * This, + /* [retval][out] */ COREWEBVIEW2_PROCESS_FAILED_KIND *processFailedKind); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Reason )( + ICoreWebView2ProcessFailedEventArgs2 * This, + /* [retval][out] */ COREWEBVIEW2_PROCESS_FAILED_REASON *reason); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ExitCode )( + ICoreWebView2ProcessFailedEventArgs2 * This, + /* [retval][out] */ int *exitCode); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ProcessDescription )( + ICoreWebView2ProcessFailedEventArgs2 * This, + /* [retval][out] */ LPWSTR *processDescription); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_FrameInfosForFailedProcess )( + ICoreWebView2ProcessFailedEventArgs2 * This, + /* [retval][out] */ ICoreWebView2FrameInfoCollection **frames); + + END_INTERFACE + } ICoreWebView2ProcessFailedEventArgs2Vtbl; + + interface ICoreWebView2ProcessFailedEventArgs2 + { + CONST_VTBL struct ICoreWebView2ProcessFailedEventArgs2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ProcessFailedEventArgs2_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ProcessFailedEventArgs2_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ProcessFailedEventArgs2_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ProcessFailedEventArgs2_get_ProcessFailedKind(This,processFailedKind) \ + ( (This)->lpVtbl -> get_ProcessFailedKind(This,processFailedKind) ) + + +#define ICoreWebView2ProcessFailedEventArgs2_get_Reason(This,reason) \ + ( (This)->lpVtbl -> get_Reason(This,reason) ) + +#define ICoreWebView2ProcessFailedEventArgs2_get_ExitCode(This,exitCode) \ + ( (This)->lpVtbl -> get_ExitCode(This,exitCode) ) + +#define ICoreWebView2ProcessFailedEventArgs2_get_ProcessDescription(This,processDescription) \ + ( (This)->lpVtbl -> get_ProcessDescription(This,processDescription) ) + +#define ICoreWebView2ProcessFailedEventArgs2_get_FrameInfosForFailedProcess(This,frames) \ + ( (This)->lpVtbl -> get_FrameInfosForFailedProcess(This,frames) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ProcessFailedEventArgs2_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ProcessFailedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2ProcessFailedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ProcessFailedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ProcessFailedEventHandler = {0x79e0aea4,0x990b,0x42d9,{0xaa,0x1d,0x0f,0xcc,0x2e,0x5b,0xc7,0xf1}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("79e0aea4-990b-42d9-aa1d-0fcc2e5bc7f1") + ICoreWebView2ProcessFailedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2ProcessFailedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ProcessFailedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ProcessFailedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ProcessFailedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ProcessFailedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2ProcessFailedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2ProcessFailedEventArgs *args); + + END_INTERFACE + } ICoreWebView2ProcessFailedEventHandlerVtbl; + + interface ICoreWebView2ProcessFailedEventHandler + { + CONST_VTBL struct ICoreWebView2ProcessFailedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ProcessFailedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ProcessFailedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ProcessFailedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ProcessFailedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ProcessFailedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2RasterizationScaleChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2RasterizationScaleChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2RasterizationScaleChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2RasterizationScaleChangedEventHandler = {0x9c98c8b1,0xac53,0x427e,{0xa3,0x45,0x30,0x49,0xb5,0x52,0x4b,0xbe}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("9c98c8b1-ac53-427e-a345-3049b5524bbe") + ICoreWebView2RasterizationScaleChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2RasterizationScaleChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2RasterizationScaleChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2RasterizationScaleChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2RasterizationScaleChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2RasterizationScaleChangedEventHandler * This, + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2RasterizationScaleChangedEventHandlerVtbl; + + interface ICoreWebView2RasterizationScaleChangedEventHandler + { + CONST_VTBL struct ICoreWebView2RasterizationScaleChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2RasterizationScaleChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2RasterizationScaleChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2RasterizationScaleChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2RasterizationScaleChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2RasterizationScaleChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ScriptDialogOpeningEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2ScriptDialogOpeningEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ScriptDialogOpeningEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ScriptDialogOpeningEventArgs = {0x7390bb70,0xabe0,0x4843,{0x95,0x29,0xf1,0x43,0xb3,0x1b,0x03,0xd6}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("7390bb70-abe0-4843-9529-f143b31b03d6") + ICoreWebView2ScriptDialogOpeningEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Uri( + /* [retval][out] */ LPWSTR *uri) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Kind( + /* [retval][out] */ COREWEBVIEW2_SCRIPT_DIALOG_KIND *kind) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Message( + /* [retval][out] */ LPWSTR *message) = 0; + + virtual HRESULT STDMETHODCALLTYPE Accept( void) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_DefaultText( + /* [retval][out] */ LPWSTR *defaultText) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ResultText( + /* [retval][out] */ LPWSTR *resultText) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_ResultText( + /* [in] */ LPCWSTR resultText) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDeferral( + /* [retval][out] */ ICoreWebView2Deferral **deferral) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ScriptDialogOpeningEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ScriptDialogOpeningEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ScriptDialogOpeningEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Uri )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [retval][out] */ LPWSTR *uri); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Kind )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_SCRIPT_DIALOG_KIND *kind); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Message )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [retval][out] */ LPWSTR *message); + + HRESULT ( STDMETHODCALLTYPE *Accept )( + ICoreWebView2ScriptDialogOpeningEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_DefaultText )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [retval][out] */ LPWSTR *defaultText); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ResultText )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [retval][out] */ LPWSTR *resultText); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ResultText )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [in] */ LPCWSTR resultText); + + HRESULT ( STDMETHODCALLTYPE *GetDeferral )( + ICoreWebView2ScriptDialogOpeningEventArgs * This, + /* [retval][out] */ ICoreWebView2Deferral **deferral); + + END_INTERFACE + } ICoreWebView2ScriptDialogOpeningEventArgsVtbl; + + interface ICoreWebView2ScriptDialogOpeningEventArgs + { + CONST_VTBL struct ICoreWebView2ScriptDialogOpeningEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ScriptDialogOpeningEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ScriptDialogOpeningEventArgs_get_Uri(This,uri) \ + ( (This)->lpVtbl -> get_Uri(This,uri) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_get_Kind(This,kind) \ + ( (This)->lpVtbl -> get_Kind(This,kind) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_get_Message(This,message) \ + ( (This)->lpVtbl -> get_Message(This,message) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_Accept(This) \ + ( (This)->lpVtbl -> Accept(This) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_get_DefaultText(This,defaultText) \ + ( (This)->lpVtbl -> get_DefaultText(This,defaultText) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_get_ResultText(This,resultText) \ + ( (This)->lpVtbl -> get_ResultText(This,resultText) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_put_ResultText(This,resultText) \ + ( (This)->lpVtbl -> put_ResultText(This,resultText) ) + +#define ICoreWebView2ScriptDialogOpeningEventArgs_GetDeferral(This,deferral) \ + ( (This)->lpVtbl -> GetDeferral(This,deferral) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ScriptDialogOpeningEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ScriptDialogOpeningEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2ScriptDialogOpeningEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ScriptDialogOpeningEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ScriptDialogOpeningEventHandler = {0xef381bf9,0xafa8,0x4e37,{0x91,0xc4,0x8a,0xc4,0x85,0x24,0xbd,0xfb}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("ef381bf9-afa8-4e37-91c4-8ac48524bdfb") + ICoreWebView2ScriptDialogOpeningEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2ScriptDialogOpeningEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ScriptDialogOpeningEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ScriptDialogOpeningEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ScriptDialogOpeningEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ScriptDialogOpeningEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2ScriptDialogOpeningEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2ScriptDialogOpeningEventArgs *args); + + END_INTERFACE + } ICoreWebView2ScriptDialogOpeningEventHandlerVtbl; + + interface ICoreWebView2ScriptDialogOpeningEventHandler + { + CONST_VTBL struct ICoreWebView2ScriptDialogOpeningEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ScriptDialogOpeningEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ScriptDialogOpeningEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ScriptDialogOpeningEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ScriptDialogOpeningEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ScriptDialogOpeningEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Settings_INTERFACE_DEFINED__ +#define __ICoreWebView2Settings_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Settings */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Settings = {0xe562e4f0,0xd7fa,0x43ac,{0x8d,0x71,0xc0,0x51,0x50,0x49,0x9f,0x00}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("e562e4f0-d7fa-43ac-8d71-c05150499f00") + ICoreWebView2Settings : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsScriptEnabled( + /* [retval][out] */ BOOL *isScriptEnabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsScriptEnabled( + /* [in] */ BOOL isScriptEnabled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsWebMessageEnabled( + /* [retval][out] */ BOOL *isWebMessageEnabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsWebMessageEnabled( + /* [in] */ BOOL isWebMessageEnabled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_AreDefaultScriptDialogsEnabled( + /* [retval][out] */ BOOL *areDefaultScriptDialogsEnabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_AreDefaultScriptDialogsEnabled( + /* [in] */ BOOL areDefaultScriptDialogsEnabled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsStatusBarEnabled( + /* [retval][out] */ BOOL *isStatusBarEnabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsStatusBarEnabled( + /* [in] */ BOOL isStatusBarEnabled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_AreDevToolsEnabled( + /* [retval][out] */ BOOL *areDevToolsEnabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_AreDevToolsEnabled( + /* [in] */ BOOL areDevToolsEnabled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_AreDefaultContextMenusEnabled( + /* [retval][out] */ BOOL *enabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_AreDefaultContextMenusEnabled( + /* [in] */ BOOL enabled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_AreHostObjectsAllowed( + /* [retval][out] */ BOOL *allowed) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_AreHostObjectsAllowed( + /* [in] */ BOOL allowed) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsZoomControlEnabled( + /* [retval][out] */ BOOL *enabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsZoomControlEnabled( + /* [in] */ BOOL enabled) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsBuiltInErrorPageEnabled( + /* [retval][out] */ BOOL *enabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_IsBuiltInErrorPageEnabled( + /* [in] */ BOOL enabled) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2SettingsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Settings * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Settings * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Settings * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsScriptEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *isScriptEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsScriptEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL isScriptEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsWebMessageEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *isWebMessageEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsWebMessageEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL isWebMessageEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDefaultScriptDialogsEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *areDefaultScriptDialogsEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDefaultScriptDialogsEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL areDefaultScriptDialogsEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsStatusBarEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *isStatusBarEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsStatusBarEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL isStatusBarEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDevToolsEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *areDevToolsEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDevToolsEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL areDevToolsEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDefaultContextMenusEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDefaultContextMenusEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreHostObjectsAllowed )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *allowed); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreHostObjectsAllowed )( + ICoreWebView2Settings * This, + /* [in] */ BOOL allowed); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsZoomControlEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsZoomControlEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsBuiltInErrorPageEnabled )( + ICoreWebView2Settings * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsBuiltInErrorPageEnabled )( + ICoreWebView2Settings * This, + /* [in] */ BOOL enabled); + + END_INTERFACE + } ICoreWebView2SettingsVtbl; + + interface ICoreWebView2Settings + { + CONST_VTBL struct ICoreWebView2SettingsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Settings_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Settings_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Settings_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Settings_get_IsScriptEnabled(This,isScriptEnabled) \ + ( (This)->lpVtbl -> get_IsScriptEnabled(This,isScriptEnabled) ) + +#define ICoreWebView2Settings_put_IsScriptEnabled(This,isScriptEnabled) \ + ( (This)->lpVtbl -> put_IsScriptEnabled(This,isScriptEnabled) ) + +#define ICoreWebView2Settings_get_IsWebMessageEnabled(This,isWebMessageEnabled) \ + ( (This)->lpVtbl -> get_IsWebMessageEnabled(This,isWebMessageEnabled) ) + +#define ICoreWebView2Settings_put_IsWebMessageEnabled(This,isWebMessageEnabled) \ + ( (This)->lpVtbl -> put_IsWebMessageEnabled(This,isWebMessageEnabled) ) + +#define ICoreWebView2Settings_get_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) \ + ( (This)->lpVtbl -> get_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) ) + +#define ICoreWebView2Settings_put_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) \ + ( (This)->lpVtbl -> put_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) ) + +#define ICoreWebView2Settings_get_IsStatusBarEnabled(This,isStatusBarEnabled) \ + ( (This)->lpVtbl -> get_IsStatusBarEnabled(This,isStatusBarEnabled) ) + +#define ICoreWebView2Settings_put_IsStatusBarEnabled(This,isStatusBarEnabled) \ + ( (This)->lpVtbl -> put_IsStatusBarEnabled(This,isStatusBarEnabled) ) + +#define ICoreWebView2Settings_get_AreDevToolsEnabled(This,areDevToolsEnabled) \ + ( (This)->lpVtbl -> get_AreDevToolsEnabled(This,areDevToolsEnabled) ) + +#define ICoreWebView2Settings_put_AreDevToolsEnabled(This,areDevToolsEnabled) \ + ( (This)->lpVtbl -> put_AreDevToolsEnabled(This,areDevToolsEnabled) ) + +#define ICoreWebView2Settings_get_AreDefaultContextMenusEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_AreDefaultContextMenusEnabled(This,enabled) ) + +#define ICoreWebView2Settings_put_AreDefaultContextMenusEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_AreDefaultContextMenusEnabled(This,enabled) ) + +#define ICoreWebView2Settings_get_AreHostObjectsAllowed(This,allowed) \ + ( (This)->lpVtbl -> get_AreHostObjectsAllowed(This,allowed) ) + +#define ICoreWebView2Settings_put_AreHostObjectsAllowed(This,allowed) \ + ( (This)->lpVtbl -> put_AreHostObjectsAllowed(This,allowed) ) + +#define ICoreWebView2Settings_get_IsZoomControlEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_IsZoomControlEnabled(This,enabled) ) + +#define ICoreWebView2Settings_put_IsZoomControlEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_IsZoomControlEnabled(This,enabled) ) + +#define ICoreWebView2Settings_get_IsBuiltInErrorPageEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_IsBuiltInErrorPageEnabled(This,enabled) ) + +#define ICoreWebView2Settings_put_IsBuiltInErrorPageEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_IsBuiltInErrorPageEnabled(This,enabled) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Settings_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Settings2_INTERFACE_DEFINED__ +#define __ICoreWebView2Settings2_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Settings2 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Settings2 = {0xee9a0f68,0xf46c,0x4e32,{0xac,0x23,0xef,0x8c,0xac,0x22,0x4d,0x2a}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("ee9a0f68-f46c-4e32-ac23-ef8cac224d2a") + ICoreWebView2Settings2 : public ICoreWebView2Settings + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_UserAgent( + /* [retval][out] */ LPWSTR *userAgent) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_UserAgent( + /* [in] */ LPCWSTR userAgent) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Settings2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Settings2 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Settings2 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Settings2 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsScriptEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *isScriptEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsScriptEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL isScriptEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsWebMessageEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *isWebMessageEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsWebMessageEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL isWebMessageEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDefaultScriptDialogsEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *areDefaultScriptDialogsEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDefaultScriptDialogsEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL areDefaultScriptDialogsEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsStatusBarEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *isStatusBarEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsStatusBarEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL isStatusBarEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDevToolsEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *areDevToolsEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDevToolsEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL areDevToolsEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDefaultContextMenusEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDefaultContextMenusEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreHostObjectsAllowed )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *allowed); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreHostObjectsAllowed )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL allowed); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsZoomControlEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsZoomControlEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsBuiltInErrorPageEnabled )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsBuiltInErrorPageEnabled )( + ICoreWebView2Settings2 * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_UserAgent )( + ICoreWebView2Settings2 * This, + /* [retval][out] */ LPWSTR *userAgent); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_UserAgent )( + ICoreWebView2Settings2 * This, + /* [in] */ LPCWSTR userAgent); + + END_INTERFACE + } ICoreWebView2Settings2Vtbl; + + interface ICoreWebView2Settings2 + { + CONST_VTBL struct ICoreWebView2Settings2Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Settings2_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Settings2_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Settings2_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Settings2_get_IsScriptEnabled(This,isScriptEnabled) \ + ( (This)->lpVtbl -> get_IsScriptEnabled(This,isScriptEnabled) ) + +#define ICoreWebView2Settings2_put_IsScriptEnabled(This,isScriptEnabled) \ + ( (This)->lpVtbl -> put_IsScriptEnabled(This,isScriptEnabled) ) + +#define ICoreWebView2Settings2_get_IsWebMessageEnabled(This,isWebMessageEnabled) \ + ( (This)->lpVtbl -> get_IsWebMessageEnabled(This,isWebMessageEnabled) ) + +#define ICoreWebView2Settings2_put_IsWebMessageEnabled(This,isWebMessageEnabled) \ + ( (This)->lpVtbl -> put_IsWebMessageEnabled(This,isWebMessageEnabled) ) + +#define ICoreWebView2Settings2_get_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) \ + ( (This)->lpVtbl -> get_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) ) + +#define ICoreWebView2Settings2_put_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) \ + ( (This)->lpVtbl -> put_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) ) + +#define ICoreWebView2Settings2_get_IsStatusBarEnabled(This,isStatusBarEnabled) \ + ( (This)->lpVtbl -> get_IsStatusBarEnabled(This,isStatusBarEnabled) ) + +#define ICoreWebView2Settings2_put_IsStatusBarEnabled(This,isStatusBarEnabled) \ + ( (This)->lpVtbl -> put_IsStatusBarEnabled(This,isStatusBarEnabled) ) + +#define ICoreWebView2Settings2_get_AreDevToolsEnabled(This,areDevToolsEnabled) \ + ( (This)->lpVtbl -> get_AreDevToolsEnabled(This,areDevToolsEnabled) ) + +#define ICoreWebView2Settings2_put_AreDevToolsEnabled(This,areDevToolsEnabled) \ + ( (This)->lpVtbl -> put_AreDevToolsEnabled(This,areDevToolsEnabled) ) + +#define ICoreWebView2Settings2_get_AreDefaultContextMenusEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_AreDefaultContextMenusEnabled(This,enabled) ) + +#define ICoreWebView2Settings2_put_AreDefaultContextMenusEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_AreDefaultContextMenusEnabled(This,enabled) ) + +#define ICoreWebView2Settings2_get_AreHostObjectsAllowed(This,allowed) \ + ( (This)->lpVtbl -> get_AreHostObjectsAllowed(This,allowed) ) + +#define ICoreWebView2Settings2_put_AreHostObjectsAllowed(This,allowed) \ + ( (This)->lpVtbl -> put_AreHostObjectsAllowed(This,allowed) ) + +#define ICoreWebView2Settings2_get_IsZoomControlEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_IsZoomControlEnabled(This,enabled) ) + +#define ICoreWebView2Settings2_put_IsZoomControlEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_IsZoomControlEnabled(This,enabled) ) + +#define ICoreWebView2Settings2_get_IsBuiltInErrorPageEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_IsBuiltInErrorPageEnabled(This,enabled) ) + +#define ICoreWebView2Settings2_put_IsBuiltInErrorPageEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_IsBuiltInErrorPageEnabled(This,enabled) ) + + +#define ICoreWebView2Settings2_get_UserAgent(This,userAgent) \ + ( (This)->lpVtbl -> get_UserAgent(This,userAgent) ) + +#define ICoreWebView2Settings2_put_UserAgent(This,userAgent) \ + ( (This)->lpVtbl -> put_UserAgent(This,userAgent) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Settings2_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2Settings3_INTERFACE_DEFINED__ +#define __ICoreWebView2Settings3_INTERFACE_DEFINED__ + +/* interface ICoreWebView2Settings3 */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2Settings3 = {0xfdb5ab74,0xaf33,0x4854,{0x84,0xf0,0x0a,0x63,0x1d,0xeb,0x5e,0xba}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("fdb5ab74-af33-4854-84f0-0a631deb5eba") + ICoreWebView2Settings3 : public ICoreWebView2Settings2 + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_AreBrowserAcceleratorKeysEnabled( + /* [retval][out] */ BOOL *areBrowserAcceleratorKeysEnabled) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_AreBrowserAcceleratorKeysEnabled( + /* [in] */ BOOL areBrowserAcceleratorKeysEnabled) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2Settings3Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2Settings3 * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2Settings3 * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2Settings3 * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsScriptEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *isScriptEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsScriptEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL isScriptEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsWebMessageEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *isWebMessageEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsWebMessageEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL isWebMessageEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDefaultScriptDialogsEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *areDefaultScriptDialogsEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDefaultScriptDialogsEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL areDefaultScriptDialogsEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsStatusBarEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *isStatusBarEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsStatusBarEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL isStatusBarEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDevToolsEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *areDevToolsEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDevToolsEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL areDevToolsEnabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreDefaultContextMenusEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreDefaultContextMenusEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreHostObjectsAllowed )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *allowed); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreHostObjectsAllowed )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL allowed); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsZoomControlEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsZoomControlEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsBuiltInErrorPageEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *enabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_IsBuiltInErrorPageEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL enabled); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_UserAgent )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ LPWSTR *userAgent); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_UserAgent )( + ICoreWebView2Settings3 * This, + /* [in] */ LPCWSTR userAgent); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_AreBrowserAcceleratorKeysEnabled )( + ICoreWebView2Settings3 * This, + /* [retval][out] */ BOOL *areBrowserAcceleratorKeysEnabled); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_AreBrowserAcceleratorKeysEnabled )( + ICoreWebView2Settings3 * This, + /* [in] */ BOOL areBrowserAcceleratorKeysEnabled); + + END_INTERFACE + } ICoreWebView2Settings3Vtbl; + + interface ICoreWebView2Settings3 + { + CONST_VTBL struct ICoreWebView2Settings3Vtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2Settings3_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2Settings3_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2Settings3_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2Settings3_get_IsScriptEnabled(This,isScriptEnabled) \ + ( (This)->lpVtbl -> get_IsScriptEnabled(This,isScriptEnabled) ) + +#define ICoreWebView2Settings3_put_IsScriptEnabled(This,isScriptEnabled) \ + ( (This)->lpVtbl -> put_IsScriptEnabled(This,isScriptEnabled) ) + +#define ICoreWebView2Settings3_get_IsWebMessageEnabled(This,isWebMessageEnabled) \ + ( (This)->lpVtbl -> get_IsWebMessageEnabled(This,isWebMessageEnabled) ) + +#define ICoreWebView2Settings3_put_IsWebMessageEnabled(This,isWebMessageEnabled) \ + ( (This)->lpVtbl -> put_IsWebMessageEnabled(This,isWebMessageEnabled) ) + +#define ICoreWebView2Settings3_get_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) \ + ( (This)->lpVtbl -> get_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) ) + +#define ICoreWebView2Settings3_put_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) \ + ( (This)->lpVtbl -> put_AreDefaultScriptDialogsEnabled(This,areDefaultScriptDialogsEnabled) ) + +#define ICoreWebView2Settings3_get_IsStatusBarEnabled(This,isStatusBarEnabled) \ + ( (This)->lpVtbl -> get_IsStatusBarEnabled(This,isStatusBarEnabled) ) + +#define ICoreWebView2Settings3_put_IsStatusBarEnabled(This,isStatusBarEnabled) \ + ( (This)->lpVtbl -> put_IsStatusBarEnabled(This,isStatusBarEnabled) ) + +#define ICoreWebView2Settings3_get_AreDevToolsEnabled(This,areDevToolsEnabled) \ + ( (This)->lpVtbl -> get_AreDevToolsEnabled(This,areDevToolsEnabled) ) + +#define ICoreWebView2Settings3_put_AreDevToolsEnabled(This,areDevToolsEnabled) \ + ( (This)->lpVtbl -> put_AreDevToolsEnabled(This,areDevToolsEnabled) ) + +#define ICoreWebView2Settings3_get_AreDefaultContextMenusEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_AreDefaultContextMenusEnabled(This,enabled) ) + +#define ICoreWebView2Settings3_put_AreDefaultContextMenusEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_AreDefaultContextMenusEnabled(This,enabled) ) + +#define ICoreWebView2Settings3_get_AreHostObjectsAllowed(This,allowed) \ + ( (This)->lpVtbl -> get_AreHostObjectsAllowed(This,allowed) ) + +#define ICoreWebView2Settings3_put_AreHostObjectsAllowed(This,allowed) \ + ( (This)->lpVtbl -> put_AreHostObjectsAllowed(This,allowed) ) + +#define ICoreWebView2Settings3_get_IsZoomControlEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_IsZoomControlEnabled(This,enabled) ) + +#define ICoreWebView2Settings3_put_IsZoomControlEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_IsZoomControlEnabled(This,enabled) ) + +#define ICoreWebView2Settings3_get_IsBuiltInErrorPageEnabled(This,enabled) \ + ( (This)->lpVtbl -> get_IsBuiltInErrorPageEnabled(This,enabled) ) + +#define ICoreWebView2Settings3_put_IsBuiltInErrorPageEnabled(This,enabled) \ + ( (This)->lpVtbl -> put_IsBuiltInErrorPageEnabled(This,enabled) ) + + +#define ICoreWebView2Settings3_get_UserAgent(This,userAgent) \ + ( (This)->lpVtbl -> get_UserAgent(This,userAgent) ) + +#define ICoreWebView2Settings3_put_UserAgent(This,userAgent) \ + ( (This)->lpVtbl -> put_UserAgent(This,userAgent) ) + + +#define ICoreWebView2Settings3_get_AreBrowserAcceleratorKeysEnabled(This,areBrowserAcceleratorKeysEnabled) \ + ( (This)->lpVtbl -> get_AreBrowserAcceleratorKeysEnabled(This,areBrowserAcceleratorKeysEnabled) ) + +#define ICoreWebView2Settings3_put_AreBrowserAcceleratorKeysEnabled(This,areBrowserAcceleratorKeysEnabled) \ + ( (This)->lpVtbl -> put_AreBrowserAcceleratorKeysEnabled(This,areBrowserAcceleratorKeysEnabled) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2Settings3_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2SourceChangedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2SourceChangedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2SourceChangedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2SourceChangedEventArgs = {0x31e0e545,0x1dba,0x4266,{0x89,0x14,0xf6,0x38,0x48,0xa1,0xf7,0xd7}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("31e0e545-1dba-4266-8914-f63848a1f7d7") + ICoreWebView2SourceChangedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsNewDocument( + /* [retval][out] */ BOOL *isNewDocument) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2SourceChangedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2SourceChangedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2SourceChangedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2SourceChangedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_IsNewDocument )( + ICoreWebView2SourceChangedEventArgs * This, + /* [retval][out] */ BOOL *isNewDocument); + + END_INTERFACE + } ICoreWebView2SourceChangedEventArgsVtbl; + + interface ICoreWebView2SourceChangedEventArgs + { + CONST_VTBL struct ICoreWebView2SourceChangedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2SourceChangedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2SourceChangedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2SourceChangedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2SourceChangedEventArgs_get_IsNewDocument(This,isNewDocument) \ + ( (This)->lpVtbl -> get_IsNewDocument(This,isNewDocument) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2SourceChangedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2SourceChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2SourceChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2SourceChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2SourceChangedEventHandler = {0x3c067f9f,0x5388,0x4772,{0x8b,0x48,0x79,0xf7,0xef,0x1a,0xb3,0x7c}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("3c067f9f-5388-4772-8b48-79f7ef1ab37c") + ICoreWebView2SourceChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2SourceChangedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2SourceChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2SourceChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2SourceChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2SourceChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2SourceChangedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2SourceChangedEventArgs *args); + + END_INTERFACE + } ICoreWebView2SourceChangedEventHandlerVtbl; + + interface ICoreWebView2SourceChangedEventHandler + { + CONST_VTBL struct ICoreWebView2SourceChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2SourceChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2SourceChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2SourceChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2SourceChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2SourceChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2TrySuspendCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2TrySuspendCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2TrySuspendCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2TrySuspendCompletedHandler = {0x00F206A7,0x9D17,0x4605,{0x91,0xF6,0x4E,0x8E,0x4D,0xE1,0x92,0xE3}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("00F206A7-9D17-4605-91F6-4E8E4DE192E3") + ICoreWebView2TrySuspendCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ HRESULT errorCode, + /* [in] */ BOOL isSuccessful) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2TrySuspendCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2TrySuspendCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2TrySuspendCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2TrySuspendCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2TrySuspendCompletedHandler * This, + /* [in] */ HRESULT errorCode, + /* [in] */ BOOL isSuccessful); + + END_INTERFACE + } ICoreWebView2TrySuspendCompletedHandlerVtbl; + + interface ICoreWebView2TrySuspendCompletedHandler + { + CONST_VTBL struct ICoreWebView2TrySuspendCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2TrySuspendCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2TrySuspendCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2TrySuspendCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2TrySuspendCompletedHandler_Invoke(This,errorCode,isSuccessful) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,isSuccessful) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2TrySuspendCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebMessageReceivedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2WebMessageReceivedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebMessageReceivedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebMessageReceivedEventArgs = {0x0f99a40c,0xe962,0x4207,{0x9e,0x92,0xe3,0xd5,0x42,0xef,0xf8,0x49}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("0f99a40c-e962-4207-9e92-e3d542eff849") + ICoreWebView2WebMessageReceivedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Source( + /* [retval][out] */ LPWSTR *source) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_WebMessageAsJson( + /* [retval][out] */ LPWSTR *webMessageAsJson) = 0; + + virtual HRESULT STDMETHODCALLTYPE TryGetWebMessageAsString( + /* [retval][out] */ LPWSTR *webMessageAsString) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebMessageReceivedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebMessageReceivedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebMessageReceivedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebMessageReceivedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Source )( + ICoreWebView2WebMessageReceivedEventArgs * This, + /* [retval][out] */ LPWSTR *source); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_WebMessageAsJson )( + ICoreWebView2WebMessageReceivedEventArgs * This, + /* [retval][out] */ LPWSTR *webMessageAsJson); + + HRESULT ( STDMETHODCALLTYPE *TryGetWebMessageAsString )( + ICoreWebView2WebMessageReceivedEventArgs * This, + /* [retval][out] */ LPWSTR *webMessageAsString); + + END_INTERFACE + } ICoreWebView2WebMessageReceivedEventArgsVtbl; + + interface ICoreWebView2WebMessageReceivedEventArgs + { + CONST_VTBL struct ICoreWebView2WebMessageReceivedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebMessageReceivedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebMessageReceivedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebMessageReceivedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebMessageReceivedEventArgs_get_Source(This,source) \ + ( (This)->lpVtbl -> get_Source(This,source) ) + +#define ICoreWebView2WebMessageReceivedEventArgs_get_WebMessageAsJson(This,webMessageAsJson) \ + ( (This)->lpVtbl -> get_WebMessageAsJson(This,webMessageAsJson) ) + +#define ICoreWebView2WebMessageReceivedEventArgs_TryGetWebMessageAsString(This,webMessageAsString) \ + ( (This)->lpVtbl -> TryGetWebMessageAsString(This,webMessageAsString) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebMessageReceivedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebMessageReceivedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2WebMessageReceivedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebMessageReceivedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebMessageReceivedEventHandler = {0x57213f19,0x00e6,0x49fa,{0x8e,0x07,0x89,0x8e,0xa0,0x1e,0xcb,0xd2}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("57213f19-00e6-49fa-8e07-898ea01ecbd2") + ICoreWebView2WebMessageReceivedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2WebMessageReceivedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebMessageReceivedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebMessageReceivedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebMessageReceivedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebMessageReceivedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2WebMessageReceivedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2WebMessageReceivedEventArgs *args); + + END_INTERFACE + } ICoreWebView2WebMessageReceivedEventHandlerVtbl; + + interface ICoreWebView2WebMessageReceivedEventHandler + { + CONST_VTBL struct ICoreWebView2WebMessageReceivedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebMessageReceivedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebMessageReceivedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebMessageReceivedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebMessageReceivedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebMessageReceivedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceRequest_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceRequest_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceRequest */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceRequest = {0x97055cd4,0x512c,0x4264,{0x8b,0x5f,0xe3,0xf4,0x46,0xce,0xa6,0xa5}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("97055cd4-512c-4264-8b5f-e3f446cea6a5") + ICoreWebView2WebResourceRequest : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Uri( + /* [retval][out] */ LPWSTR *uri) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Uri( + /* [in] */ LPCWSTR uri) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Method( + /* [retval][out] */ LPWSTR *method) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Method( + /* [in] */ LPCWSTR method) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Content( + /* [retval][out] */ IStream **content) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Content( + /* [in] */ IStream *content) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Headers( + /* [retval][out] */ ICoreWebView2HttpRequestHeaders **headers) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceRequestVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceRequest * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceRequest * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceRequest * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Uri )( + ICoreWebView2WebResourceRequest * This, + /* [retval][out] */ LPWSTR *uri); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Uri )( + ICoreWebView2WebResourceRequest * This, + /* [in] */ LPCWSTR uri); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Method )( + ICoreWebView2WebResourceRequest * This, + /* [retval][out] */ LPWSTR *method); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Method )( + ICoreWebView2WebResourceRequest * This, + /* [in] */ LPCWSTR method); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Content )( + ICoreWebView2WebResourceRequest * This, + /* [retval][out] */ IStream **content); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Content )( + ICoreWebView2WebResourceRequest * This, + /* [in] */ IStream *content); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Headers )( + ICoreWebView2WebResourceRequest * This, + /* [retval][out] */ ICoreWebView2HttpRequestHeaders **headers); + + END_INTERFACE + } ICoreWebView2WebResourceRequestVtbl; + + interface ICoreWebView2WebResourceRequest + { + CONST_VTBL struct ICoreWebView2WebResourceRequestVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceRequest_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceRequest_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceRequest_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceRequest_get_Uri(This,uri) \ + ( (This)->lpVtbl -> get_Uri(This,uri) ) + +#define ICoreWebView2WebResourceRequest_put_Uri(This,uri) \ + ( (This)->lpVtbl -> put_Uri(This,uri) ) + +#define ICoreWebView2WebResourceRequest_get_Method(This,method) \ + ( (This)->lpVtbl -> get_Method(This,method) ) + +#define ICoreWebView2WebResourceRequest_put_Method(This,method) \ + ( (This)->lpVtbl -> put_Method(This,method) ) + +#define ICoreWebView2WebResourceRequest_get_Content(This,content) \ + ( (This)->lpVtbl -> get_Content(This,content) ) + +#define ICoreWebView2WebResourceRequest_put_Content(This,content) \ + ( (This)->lpVtbl -> put_Content(This,content) ) + +#define ICoreWebView2WebResourceRequest_get_Headers(This,headers) \ + ( (This)->lpVtbl -> get_Headers(This,headers) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceRequest_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceRequestedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceRequestedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceRequestedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceRequestedEventArgs = {0x453e667f,0x12c7,0x49d4,{0xbe,0x6d,0xdd,0xbe,0x79,0x56,0xf5,0x7a}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("453e667f-12c7-49d4-be6d-ddbe7956f57a") + ICoreWebView2WebResourceRequestedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Request( + /* [retval][out] */ ICoreWebView2WebResourceRequest **request) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Response( + /* [retval][out] */ ICoreWebView2WebResourceResponse **response) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Response( + /* [in] */ ICoreWebView2WebResourceResponse *response) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDeferral( + /* [retval][out] */ ICoreWebView2Deferral **deferral) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ResourceContext( + /* [retval][out] */ COREWEBVIEW2_WEB_RESOURCE_CONTEXT *context) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceRequestedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceRequestedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceRequestedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceRequestedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Request )( + ICoreWebView2WebResourceRequestedEventArgs * This, + /* [retval][out] */ ICoreWebView2WebResourceRequest **request); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Response )( + ICoreWebView2WebResourceRequestedEventArgs * This, + /* [retval][out] */ ICoreWebView2WebResourceResponse **response); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Response )( + ICoreWebView2WebResourceRequestedEventArgs * This, + /* [in] */ ICoreWebView2WebResourceResponse *response); + + HRESULT ( STDMETHODCALLTYPE *GetDeferral )( + ICoreWebView2WebResourceRequestedEventArgs * This, + /* [retval][out] */ ICoreWebView2Deferral **deferral); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ResourceContext )( + ICoreWebView2WebResourceRequestedEventArgs * This, + /* [retval][out] */ COREWEBVIEW2_WEB_RESOURCE_CONTEXT *context); + + END_INTERFACE + } ICoreWebView2WebResourceRequestedEventArgsVtbl; + + interface ICoreWebView2WebResourceRequestedEventArgs + { + CONST_VTBL struct ICoreWebView2WebResourceRequestedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceRequestedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceRequestedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceRequestedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceRequestedEventArgs_get_Request(This,request) \ + ( (This)->lpVtbl -> get_Request(This,request) ) + +#define ICoreWebView2WebResourceRequestedEventArgs_get_Response(This,response) \ + ( (This)->lpVtbl -> get_Response(This,response) ) + +#define ICoreWebView2WebResourceRequestedEventArgs_put_Response(This,response) \ + ( (This)->lpVtbl -> put_Response(This,response) ) + +#define ICoreWebView2WebResourceRequestedEventArgs_GetDeferral(This,deferral) \ + ( (This)->lpVtbl -> GetDeferral(This,deferral) ) + +#define ICoreWebView2WebResourceRequestedEventArgs_get_ResourceContext(This,context) \ + ( (This)->lpVtbl -> get_ResourceContext(This,context) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceRequestedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceRequestedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceRequestedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceRequestedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceRequestedEventHandler = {0xab00b74c,0x15f1,0x4646,{0x80,0xe8,0xe7,0x63,0x41,0xd2,0x5d,0x71}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("ab00b74c-15f1-4646-80e8-e76341d25d71") + ICoreWebView2WebResourceRequestedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2WebResourceRequestedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceRequestedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceRequestedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceRequestedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceRequestedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2WebResourceRequestedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2WebResourceRequestedEventArgs *args); + + END_INTERFACE + } ICoreWebView2WebResourceRequestedEventHandlerVtbl; + + interface ICoreWebView2WebResourceRequestedEventHandler + { + CONST_VTBL struct ICoreWebView2WebResourceRequestedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceRequestedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceRequestedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceRequestedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceRequestedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceRequestedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponse_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceResponse_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceResponse */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceResponse = {0xaafcc94f,0xfa27,0x48fd,{0x97,0xdf,0x83,0x0e,0xf7,0x5a,0xae,0xc9}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("aafcc94f-fa27-48fd-97df-830ef75aaec9") + ICoreWebView2WebResourceResponse : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Content( + /* [retval][out] */ IStream **content) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_Content( + /* [in] */ IStream *content) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Headers( + /* [retval][out] */ ICoreWebView2HttpResponseHeaders **headers) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_StatusCode( + /* [retval][out] */ int *statusCode) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_StatusCode( + /* [in] */ int statusCode) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ReasonPhrase( + /* [retval][out] */ LPWSTR *reasonPhrase) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_ReasonPhrase( + /* [in] */ LPCWSTR reasonPhrase) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceResponseVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceResponse * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceResponse * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceResponse * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Content )( + ICoreWebView2WebResourceResponse * This, + /* [retval][out] */ IStream **content); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_Content )( + ICoreWebView2WebResourceResponse * This, + /* [in] */ IStream *content); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Headers )( + ICoreWebView2WebResourceResponse * This, + /* [retval][out] */ ICoreWebView2HttpResponseHeaders **headers); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_StatusCode )( + ICoreWebView2WebResourceResponse * This, + /* [retval][out] */ int *statusCode); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_StatusCode )( + ICoreWebView2WebResourceResponse * This, + /* [in] */ int statusCode); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ReasonPhrase )( + ICoreWebView2WebResourceResponse * This, + /* [retval][out] */ LPWSTR *reasonPhrase); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_ReasonPhrase )( + ICoreWebView2WebResourceResponse * This, + /* [in] */ LPCWSTR reasonPhrase); + + END_INTERFACE + } ICoreWebView2WebResourceResponseVtbl; + + interface ICoreWebView2WebResourceResponse + { + CONST_VTBL struct ICoreWebView2WebResourceResponseVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceResponse_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceResponse_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceResponse_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceResponse_get_Content(This,content) \ + ( (This)->lpVtbl -> get_Content(This,content) ) + +#define ICoreWebView2WebResourceResponse_put_Content(This,content) \ + ( (This)->lpVtbl -> put_Content(This,content) ) + +#define ICoreWebView2WebResourceResponse_get_Headers(This,headers) \ + ( (This)->lpVtbl -> get_Headers(This,headers) ) + +#define ICoreWebView2WebResourceResponse_get_StatusCode(This,statusCode) \ + ( (This)->lpVtbl -> get_StatusCode(This,statusCode) ) + +#define ICoreWebView2WebResourceResponse_put_StatusCode(This,statusCode) \ + ( (This)->lpVtbl -> put_StatusCode(This,statusCode) ) + +#define ICoreWebView2WebResourceResponse_get_ReasonPhrase(This,reasonPhrase) \ + ( (This)->lpVtbl -> get_ReasonPhrase(This,reasonPhrase) ) + +#define ICoreWebView2WebResourceResponse_put_ReasonPhrase(This,reasonPhrase) \ + ( (This)->lpVtbl -> put_ReasonPhrase(This,reasonPhrase) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceResponse_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseReceivedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceResponseReceivedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceResponseReceivedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceResponseReceivedEventHandler = {0x7DE9898A,0x24F5,0x40C3,{0xA2,0xDE,0xD4,0xF4,0x58,0xE6,0x98,0x28}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("7DE9898A-24F5-40C3-A2DE-D4F458E69828") + ICoreWebView2WebResourceResponseReceivedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2WebResourceResponseReceivedEventArgs *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceResponseReceivedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceResponseReceivedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceResponseReceivedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceResponseReceivedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2WebResourceResponseReceivedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ ICoreWebView2WebResourceResponseReceivedEventArgs *args); + + END_INTERFACE + } ICoreWebView2WebResourceResponseReceivedEventHandlerVtbl; + + interface ICoreWebView2WebResourceResponseReceivedEventHandler + { + CONST_VTBL struct ICoreWebView2WebResourceResponseReceivedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceResponseReceivedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceResponseReceivedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceResponseReceivedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceResponseReceivedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceResponseReceivedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseReceivedEventArgs_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceResponseReceivedEventArgs_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceResponseReceivedEventArgs */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceResponseReceivedEventArgs = {0xD1DB483D,0x6796,0x4B8B,{0x80,0xFC,0x13,0x71,0x2B,0xB7,0x16,0xF4}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("D1DB483D-6796-4B8B-80FC-13712BB716F4") + ICoreWebView2WebResourceResponseReceivedEventArgs : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Request( + /* [retval][out] */ ICoreWebView2WebResourceRequest **request) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Response( + /* [retval][out] */ ICoreWebView2WebResourceResponseView **response) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceResponseReceivedEventArgsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceResponseReceivedEventArgs * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceResponseReceivedEventArgs * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceResponseReceivedEventArgs * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Request )( + ICoreWebView2WebResourceResponseReceivedEventArgs * This, + /* [retval][out] */ ICoreWebView2WebResourceRequest **request); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Response )( + ICoreWebView2WebResourceResponseReceivedEventArgs * This, + /* [retval][out] */ ICoreWebView2WebResourceResponseView **response); + + END_INTERFACE + } ICoreWebView2WebResourceResponseReceivedEventArgsVtbl; + + interface ICoreWebView2WebResourceResponseReceivedEventArgs + { + CONST_VTBL struct ICoreWebView2WebResourceResponseReceivedEventArgsVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceResponseReceivedEventArgs_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceResponseReceivedEventArgs_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceResponseReceivedEventArgs_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceResponseReceivedEventArgs_get_Request(This,request) \ + ( (This)->lpVtbl -> get_Request(This,request) ) + +#define ICoreWebView2WebResourceResponseReceivedEventArgs_get_Response(This,response) \ + ( (This)->lpVtbl -> get_Response(This,response) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceResponseReceivedEventArgs_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseView_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceResponseView_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceResponseView */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceResponseView = {0x79701053,0x7759,0x4162,{0x8F,0x7D,0xF1,0xB3,0xF0,0x84,0x92,0x8D}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("79701053-7759-4162-8F7D-F1B3F084928D") + ICoreWebView2WebResourceResponseView : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Headers( + /* [retval][out] */ ICoreWebView2HttpResponseHeaders **headers) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_StatusCode( + /* [retval][out] */ int *statusCode) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ReasonPhrase( + /* [retval][out] */ LPWSTR *reasonPhrase) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetContent( + /* [in] */ ICoreWebView2WebResourceResponseViewGetContentCompletedHandler *handler) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceResponseViewVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceResponseView * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceResponseView * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceResponseView * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Headers )( + ICoreWebView2WebResourceResponseView * This, + /* [retval][out] */ ICoreWebView2HttpResponseHeaders **headers); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_StatusCode )( + ICoreWebView2WebResourceResponseView * This, + /* [retval][out] */ int *statusCode); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ReasonPhrase )( + ICoreWebView2WebResourceResponseView * This, + /* [retval][out] */ LPWSTR *reasonPhrase); + + HRESULT ( STDMETHODCALLTYPE *GetContent )( + ICoreWebView2WebResourceResponseView * This, + /* [in] */ ICoreWebView2WebResourceResponseViewGetContentCompletedHandler *handler); + + END_INTERFACE + } ICoreWebView2WebResourceResponseViewVtbl; + + interface ICoreWebView2WebResourceResponseView + { + CONST_VTBL struct ICoreWebView2WebResourceResponseViewVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceResponseView_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceResponseView_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceResponseView_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceResponseView_get_Headers(This,headers) \ + ( (This)->lpVtbl -> get_Headers(This,headers) ) + +#define ICoreWebView2WebResourceResponseView_get_StatusCode(This,statusCode) \ + ( (This)->lpVtbl -> get_StatusCode(This,statusCode) ) + +#define ICoreWebView2WebResourceResponseView_get_ReasonPhrase(This,reasonPhrase) \ + ( (This)->lpVtbl -> get_ReasonPhrase(This,reasonPhrase) ) + +#define ICoreWebView2WebResourceResponseView_GetContent(This,handler) \ + ( (This)->lpVtbl -> GetContent(This,handler) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceResponseView_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WebResourceResponseViewGetContentCompletedHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WebResourceResponseViewGetContentCompletedHandler = {0x875738E1,0x9FA2,0x40E3,{0x8B,0x74,0x2E,0x89,0x72,0xDD,0x6F,0xE7}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("875738E1-9FA2-40E3-8B74-2E8972DD6FE7") + ICoreWebView2WebResourceResponseViewGetContentCompletedHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ HRESULT errorCode, + /* [in] */ IStream *content) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WebResourceResponseViewGetContentCompletedHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WebResourceResponseViewGetContentCompletedHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WebResourceResponseViewGetContentCompletedHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WebResourceResponseViewGetContentCompletedHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2WebResourceResponseViewGetContentCompletedHandler * This, + /* [in] */ HRESULT errorCode, + /* [in] */ IStream *content); + + END_INTERFACE + } ICoreWebView2WebResourceResponseViewGetContentCompletedHandlerVtbl; + + interface ICoreWebView2WebResourceResponseViewGetContentCompletedHandler + { + CONST_VTBL struct ICoreWebView2WebResourceResponseViewGetContentCompletedHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_Invoke(This,errorCode,content) \ + ( (This)->lpVtbl -> Invoke(This,errorCode,content) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WebResourceResponseViewGetContentCompletedHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WindowCloseRequestedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2WindowCloseRequestedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WindowCloseRequestedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WindowCloseRequestedEventHandler = {0x5c19e9e0,0x092f,0x486b,{0xaf,0xfa,0xca,0x82,0x31,0x91,0x30,0x39}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("5c19e9e0-092f-486b-affa-ca8231913039") + ICoreWebView2WindowCloseRequestedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WindowCloseRequestedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WindowCloseRequestedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WindowCloseRequestedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WindowCloseRequestedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2WindowCloseRequestedEventHandler * This, + /* [in] */ ICoreWebView2 *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2WindowCloseRequestedEventHandlerVtbl; + + interface ICoreWebView2WindowCloseRequestedEventHandler + { + CONST_VTBL struct ICoreWebView2WindowCloseRequestedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WindowCloseRequestedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WindowCloseRequestedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WindowCloseRequestedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WindowCloseRequestedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WindowCloseRequestedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2WindowFeatures_INTERFACE_DEFINED__ +#define __ICoreWebView2WindowFeatures_INTERFACE_DEFINED__ + +/* interface ICoreWebView2WindowFeatures */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2WindowFeatures = {0x5eaf559f,0xb46e,0x4397,{0x88,0x60,0xe4,0x22,0xf2,0x87,0xff,0x1e}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("5eaf559f-b46e-4397-8860-e422f287ff1e") + ICoreWebView2WindowFeatures : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HasPosition( + /* [retval][out] */ BOOL *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HasSize( + /* [retval][out] */ BOOL *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Left( + /* [retval][out] */ UINT32 *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Top( + /* [retval][out] */ UINT32 *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Height( + /* [retval][out] */ UINT32 *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Width( + /* [retval][out] */ UINT32 *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ShouldDisplayMenuBar( + /* [retval][out] */ BOOL *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ShouldDisplayStatus( + /* [retval][out] */ BOOL *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ShouldDisplayToolbar( + /* [retval][out] */ BOOL *value) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ShouldDisplayScrollBars( + /* [retval][out] */ BOOL *value) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2WindowFeaturesVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2WindowFeatures * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2WindowFeatures * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2WindowFeatures * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HasPosition )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ BOOL *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_HasSize )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ BOOL *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Left )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ UINT32 *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Top )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ UINT32 *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Height )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ UINT32 *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_Width )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ UINT32 *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ShouldDisplayMenuBar )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ BOOL *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ShouldDisplayStatus )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ BOOL *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ShouldDisplayToolbar )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ BOOL *value); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_ShouldDisplayScrollBars )( + ICoreWebView2WindowFeatures * This, + /* [retval][out] */ BOOL *value); + + END_INTERFACE + } ICoreWebView2WindowFeaturesVtbl; + + interface ICoreWebView2WindowFeatures + { + CONST_VTBL struct ICoreWebView2WindowFeaturesVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2WindowFeatures_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2WindowFeatures_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2WindowFeatures_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2WindowFeatures_get_HasPosition(This,value) \ + ( (This)->lpVtbl -> get_HasPosition(This,value) ) + +#define ICoreWebView2WindowFeatures_get_HasSize(This,value) \ + ( (This)->lpVtbl -> get_HasSize(This,value) ) + +#define ICoreWebView2WindowFeatures_get_Left(This,value) \ + ( (This)->lpVtbl -> get_Left(This,value) ) + +#define ICoreWebView2WindowFeatures_get_Top(This,value) \ + ( (This)->lpVtbl -> get_Top(This,value) ) + +#define ICoreWebView2WindowFeatures_get_Height(This,value) \ + ( (This)->lpVtbl -> get_Height(This,value) ) + +#define ICoreWebView2WindowFeatures_get_Width(This,value) \ + ( (This)->lpVtbl -> get_Width(This,value) ) + +#define ICoreWebView2WindowFeatures_get_ShouldDisplayMenuBar(This,value) \ + ( (This)->lpVtbl -> get_ShouldDisplayMenuBar(This,value) ) + +#define ICoreWebView2WindowFeatures_get_ShouldDisplayStatus(This,value) \ + ( (This)->lpVtbl -> get_ShouldDisplayStatus(This,value) ) + +#define ICoreWebView2WindowFeatures_get_ShouldDisplayToolbar(This,value) \ + ( (This)->lpVtbl -> get_ShouldDisplayToolbar(This,value) ) + +#define ICoreWebView2WindowFeatures_get_ShouldDisplayScrollBars(This,value) \ + ( (This)->lpVtbl -> get_ShouldDisplayScrollBars(This,value) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2WindowFeatures_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2ZoomFactorChangedEventHandler_INTERFACE_DEFINED__ +#define __ICoreWebView2ZoomFactorChangedEventHandler_INTERFACE_DEFINED__ + +/* interface ICoreWebView2ZoomFactorChangedEventHandler */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2ZoomFactorChangedEventHandler = {0xb52d71d6,0xc4df,0x4543,{0xa9,0x0c,0x64,0xa3,0xe6,0x0f,0x38,0xcb}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("b52d71d6-c4df-4543-a90c-64a3e60f38cb") + ICoreWebView2ZoomFactorChangedEventHandler : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Invoke( + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ IUnknown *args) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2ZoomFactorChangedEventHandlerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2ZoomFactorChangedEventHandler * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2ZoomFactorChangedEventHandler * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2ZoomFactorChangedEventHandler * This); + + HRESULT ( STDMETHODCALLTYPE *Invoke )( + ICoreWebView2ZoomFactorChangedEventHandler * This, + /* [in] */ ICoreWebView2Controller *sender, + /* [in] */ IUnknown *args); + + END_INTERFACE + } ICoreWebView2ZoomFactorChangedEventHandlerVtbl; + + interface ICoreWebView2ZoomFactorChangedEventHandler + { + CONST_VTBL struct ICoreWebView2ZoomFactorChangedEventHandlerVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2ZoomFactorChangedEventHandler_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2ZoomFactorChangedEventHandler_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2ZoomFactorChangedEventHandler_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2ZoomFactorChangedEventHandler_Invoke(This,sender,args) \ + ( (This)->lpVtbl -> Invoke(This,sender,args) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2ZoomFactorChangedEventHandler_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2CompositionControllerInterop_INTERFACE_DEFINED__ +#define __ICoreWebView2CompositionControllerInterop_INTERFACE_DEFINED__ + +/* interface ICoreWebView2CompositionControllerInterop */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2CompositionControllerInterop = {0x8e9922ce,0x9c80,0x42e6,{0xba,0xd7,0xfc,0xeb,0xf2,0x91,0xa4,0x95}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("8e9922ce-9c80-42e6-bad7-fcebf291a495") + ICoreWebView2CompositionControllerInterop : public IUnknown + { + public: + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_UIAProvider( + /* [retval][out] */ IUnknown **provider) = 0; + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RootVisualTarget( + /* [retval][out] */ IUnknown **target) = 0; + + virtual /* [propput] */ HRESULT STDMETHODCALLTYPE put_RootVisualTarget( + /* [in] */ IUnknown *target) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2CompositionControllerInteropVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2CompositionControllerInterop * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2CompositionControllerInterop * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2CompositionControllerInterop * This); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_UIAProvider )( + ICoreWebView2CompositionControllerInterop * This, + /* [retval][out] */ IUnknown **provider); + + /* [propget] */ HRESULT ( STDMETHODCALLTYPE *get_RootVisualTarget )( + ICoreWebView2CompositionControllerInterop * This, + /* [retval][out] */ IUnknown **target); + + /* [propput] */ HRESULT ( STDMETHODCALLTYPE *put_RootVisualTarget )( + ICoreWebView2CompositionControllerInterop * This, + /* [in] */ IUnknown *target); + + END_INTERFACE + } ICoreWebView2CompositionControllerInteropVtbl; + + interface ICoreWebView2CompositionControllerInterop + { + CONST_VTBL struct ICoreWebView2CompositionControllerInteropVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2CompositionControllerInterop_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2CompositionControllerInterop_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2CompositionControllerInterop_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2CompositionControllerInterop_get_UIAProvider(This,provider) \ + ( (This)->lpVtbl -> get_UIAProvider(This,provider) ) + +#define ICoreWebView2CompositionControllerInterop_get_RootVisualTarget(This,target) \ + ( (This)->lpVtbl -> get_RootVisualTarget(This,target) ) + +#define ICoreWebView2CompositionControllerInterop_put_RootVisualTarget(This,target) \ + ( (This)->lpVtbl -> put_RootVisualTarget(This,target) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2CompositionControllerInterop_INTERFACE_DEFINED__ */ + + +#ifndef __ICoreWebView2EnvironmentInterop_INTERFACE_DEFINED__ +#define __ICoreWebView2EnvironmentInterop_INTERFACE_DEFINED__ + +/* interface ICoreWebView2EnvironmentInterop */ +/* [unique][object][uuid] */ + + +EXTERN_C __declspec(selectany) const IID IID_ICoreWebView2EnvironmentInterop = {0xee503a63,0xc1e2,0x4fbf,{0x8a,0x4d,0x82,0x4e,0x95,0xf8,0xbb,0x13}}; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("ee503a63-c1e2-4fbf-8a4d-824e95f8bb13") + ICoreWebView2EnvironmentInterop : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetProviderForHwnd( + /* [in] */ HWND hwnd, + /* [retval][out] */ IUnknown **provider) = 0; + + }; + + +#else /* C style interface */ + + typedef struct ICoreWebView2EnvironmentInteropVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + ICoreWebView2EnvironmentInterop * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + ICoreWebView2EnvironmentInterop * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + ICoreWebView2EnvironmentInterop * This); + + HRESULT ( STDMETHODCALLTYPE *GetProviderForHwnd )( + ICoreWebView2EnvironmentInterop * This, + /* [in] */ HWND hwnd, + /* [retval][out] */ IUnknown **provider); + + END_INTERFACE + } ICoreWebView2EnvironmentInteropVtbl; + + interface ICoreWebView2EnvironmentInterop + { + CONST_VTBL struct ICoreWebView2EnvironmentInteropVtbl *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ICoreWebView2EnvironmentInterop_QueryInterface(This,riid,ppvObject) \ + ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) + +#define ICoreWebView2EnvironmentInterop_AddRef(This) \ + ( (This)->lpVtbl -> AddRef(This) ) + +#define ICoreWebView2EnvironmentInterop_Release(This) \ + ( (This)->lpVtbl -> Release(This) ) + + +#define ICoreWebView2EnvironmentInterop_GetProviderForHwnd(This,hwnd,provider) \ + ( (This)->lpVtbl -> GetProviderForHwnd(This,hwnd,provider) ) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __ICoreWebView2EnvironmentInterop_INTERFACE_DEFINED__ */ + +#endif /* __WebView2_LIBRARY_DEFINED__ */ + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/v2/internal/ffenestri/windows/WebView2EnvironmentOptions.h b/v2/internal/ffenestri/windows/WebView2EnvironmentOptions.h new file mode 100644 index 000000000..6475ce58a --- /dev/null +++ b/v2/internal/ffenestri/windows/WebView2EnvironmentOptions.h @@ -0,0 +1,144 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef __core_webview2_environment_options_h__ +#define __core_webview2_environment_options_h__ + +#include +#include + +#include "webview2.h" +#define CORE_WEBVIEW_TARGET_PRODUCT_VERSION L"91.0.864.35" + +#define COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(p) \ + public: \ + HRESULT STDMETHODCALLTYPE get_##p(LPWSTR* value) override { \ + if (!value) \ + return E_POINTER; \ + *value = m_##p.Copy(); \ + if ((*value == nullptr) && (m_##p.Get() != nullptr)) \ + return HRESULT_FROM_WIN32(GetLastError()); \ + return S_OK; \ + } \ + HRESULT STDMETHODCALLTYPE put_##p(LPCWSTR value) override { \ + LPCWSTR result = m_##p.Set(value); \ + if ((result == nullptr) && (value != nullptr)) \ + return HRESULT_FROM_WIN32(GetLastError()); \ + return S_OK; \ + } \ + \ + protected: \ + AutoCoMemString m_##p; + +#define COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY(p) \ + public: \ + HRESULT STDMETHODCALLTYPE get_##p(BOOL* value) override { \ + if (!value) \ + return E_POINTER; \ + *value = m_##p; \ + return S_OK; \ + } \ + HRESULT STDMETHODCALLTYPE put_##p(BOOL value) override { \ + m_##p = value; \ + return S_OK; \ + } \ + \ + protected: \ + BOOL m_##p = FALSE; + +// This is a base COM class that implements ICoreWebView2EnvironmentOptions. +template +class CoreWebView2EnvironmentOptionsBase + : public Microsoft::WRL::Implements< + Microsoft::WRL::RuntimeClassFlags, + ICoreWebView2EnvironmentOptions> { + public: + CoreWebView2EnvironmentOptionsBase() { + // Initialize the target compatible browser version value to the version of + // the browser binaries corresponding to this version of the SDK. + m_TargetCompatibleBrowserVersion.Set(CORE_WEBVIEW_TARGET_PRODUCT_VERSION); + } + + protected: + ~CoreWebView2EnvironmentOptionsBase(){}; + + class AutoCoMemString { + public: + AutoCoMemString() {} + ~AutoCoMemString() { Release(); } + void Release() { + if (m_string) { + deallocate_fn(m_string); + m_string = nullptr; + } + } + + LPCWSTR Set(LPCWSTR str) { + Release(); + if (str) { + m_string = MakeCoMemString(str); + } + return m_string; + } + LPCWSTR Get() { return m_string; } + LPWSTR Copy() { + if (m_string) + return MakeCoMemString(m_string); + return nullptr; + } + + protected: + LPWSTR MakeCoMemString(LPCWSTR source) { + const size_t length = wcslen(source); + const size_t bytes = (length + 1) * sizeof(*source); + // Ensure we didn't overflow during our size calculation. + if (bytes <= length) { + return nullptr; + } + + wchar_t* result = reinterpret_cast(allocate_fn(bytes)); + if (result) + memcpy(result, source, bytes); + + return result; + } + + LPWSTR m_string = nullptr; + }; + + COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(AdditionalBrowserArguments) + COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(Language) + COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(TargetCompatibleBrowserVersion) + COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY( + AllowSingleSignOnUsingOSPrimaryAccount) +}; + +template +class CoreWebView2EnvironmentOptionsBaseClass + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags, + CoreWebView2EnvironmentOptionsBase> { + public: + CoreWebView2EnvironmentOptionsBaseClass() {} + + protected: + ~CoreWebView2EnvironmentOptionsBaseClass() override{}; +}; + +typedef CoreWebView2EnvironmentOptionsBaseClass + CoreWebView2EnvironmentOptions; + +#endif // __core_webview2_environment_options_h__ diff --git a/v2/internal/ffenestri/windows/scripts/README.md b/v2/internal/ffenestri/windows/scripts/README.md new file mode 100644 index 000000000..634e3596b --- /dev/null +++ b/v2/internal/ffenestri/windows/scripts/README.md @@ -0,0 +1,11 @@ +# Build + +This script will download the given webview2 sdk version and copy out the files necessary for building Wails apps. + +## Prerequistes + + - nuget + +## Usage + +`updatesdk.bat ` diff --git a/v2/internal/ffenestri/windows/scripts/sdkversion.txt b/v2/internal/ffenestri/windows/scripts/sdkversion.txt new file mode 100644 index 000000000..e4c251ea5 --- /dev/null +++ b/v2/internal/ffenestri/windows/scripts/sdkversion.txt @@ -0,0 +1 @@ +The version of WebView2 SDK used: 1.0.992.28 diff --git a/v2/internal/ffenestri/windows/scripts/updatesdk.bat b/v2/internal/ffenestri/windows/scripts/updatesdk.bat new file mode 100644 index 000000000..780c09128 --- /dev/null +++ b/v2/internal/ffenestri/windows/scripts/updatesdk.bat @@ -0,0 +1,18 @@ +@echo off +IF %1.==. GOTO NoVersion +nuget install microsoft.web.webview2 -Version %1 -OutputDirectory . >NUL || goto :eof +echo Downloaded microsoft.web.webview2.%1 + +set sdk_version=%1 +set native_dir="%~dp0\microsoft.web.webview2.%sdk_version%\build\native" +copy "%native_dir%\include\*.h" .. >NUL +copy "%native_dir%\x64\WebView2Loader.dll" "..\x64" >NUL +@REM @rd /S /Q "microsoft.web.webview2.%sdk_version%" +del /s version.txt >nul 2>&1 +echo The version of WebView2 SDK used: %sdk_version% > sdkversion.txt +echo SDK updated to %sdk_version% +goto :eof + +:NoVersion + echo Please provide a version number, EG: 1.0.664.37 + goto :eof diff --git a/v2/internal/ffenestri/windows/wv2runtime/browser.go b/v2/internal/ffenestri/windows/wv2runtime/browser.go new file mode 100644 index 000000000..25f19f2e0 --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/browser.go @@ -0,0 +1,23 @@ +// +build wv2runtime.browser + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + confirmed, err := webview2runtime.Confirm("This application requires the WebView2 runtime. Press OK to open the download page. Minimum version required: "+MinimumRuntimeVersion, "Missing Requirements") + if err != nil { + return err + } + if confirmed { + err = webview2runtime.OpenInstallerDownloadWebpage() + if err != nil { + return err + } + } + + return fmt.Errorf("webview2 runtime not installed") +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/download.go b/v2/internal/ffenestri/windows/wv2runtime/download.go new file mode 100644 index 000000000..cfb2aa197 --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/download.go @@ -0,0 +1,35 @@ +// +build !wv2runtime.error +// +build !wv2runtime.browser +// +build !wv2runtime.embed + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + message := "The WebView2 runtime is required. " + if installStatus == needsUpdating { + message = "The Webview2 runtime needs updating. " + } + message += "Press Ok to download and install. Note: The installer will download silently so please wait." + confirmed, err := webview2runtime.Confirm(message, "Missing Requirements") + if err != nil { + return err + } + if !confirmed { + return fmt.Errorf("webview2 runtime not installed") + } + installedCorrectly, err := webview2runtime.InstallUsingBootstrapper() + if err != nil { + _ = webview2runtime.Error(err.Error(), "Error") + return err + } + if !installedCorrectly { + err = webview2runtime.Error("The runtime failed to install correctly. Please try again.", "Error") + return err + } + return nil +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/embed.go b/v2/internal/ffenestri/windows/wv2runtime/embed.go new file mode 100644 index 000000000..99a472d48 --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/embed.go @@ -0,0 +1,33 @@ +// +build wv2runtime.embed + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + message := "The WebView2 runtime is required. " + if installStatus == needsUpdating { + message = "The Webview2 runtime needs updating. " + } + message += "Press Ok to install." + confirmed, err := webview2runtime.Confirm(message, "Missing Requirements") + if err != nil { + return err + } + if !confirmed { + return fmt.Errorf("webview2 runtime not installed") + } + installedCorrectly, err := webview2runtime.InstallUsingEmbeddedBootstrapper() + if err != nil { + _ = webview2runtime.Error(err.Error(), "Error") + return err + } + if !installedCorrectly { + err = webview2runtime.Error("The runtime failed to install correctly. Please try again.", "Error") + return err + } + return nil +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/error.go b/v2/internal/ffenestri/windows/wv2runtime/error.go new file mode 100644 index 000000000..f03d8c81c --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/error.go @@ -0,0 +1,13 @@ +// +build wv2runtime.error + +package wv2runtime + +import ( + "fmt" + "github.com/leaanthony/webview2runtime" +) + +func doInstallationStrategy(installStatus installationStatus) error { + _ = webview2runtime.Error("The WebView2 runtime is required to run this application. Please contact your system administrator.", "Error") + return fmt.Errorf("webview2 runtime not installed") +} diff --git a/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go b/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go new file mode 100644 index 000000000..7326d54b1 --- /dev/null +++ b/v2/internal/ffenestri/windows/wv2runtime/wv2runtime.go @@ -0,0 +1,35 @@ +package wv2runtime + +import ( + "github.com/leaanthony/go-webview2/webviewloader" + "github.com/leaanthony/webview2runtime" +) + +const MinimumRuntimeVersion string = "91.0.992.28" + +type installationStatus int + +const ( + needsInstalling installationStatus = iota + needsUpdating + installed +) + +func Process() (*webview2runtime.Info, error) { + installStatus := needsInstalling + installedVersion := webview2runtime.GetInstalledVersion() + if installedVersion != nil { + installStatus = installed + compareResult, err := webviewloader.CompareBrowserVersions(installedVersion.Version, MinimumRuntimeVersion) + if err != nil { + return nil, err + } + updateRequired := compareResult == -1 + // Installed and does not require updating + if !updateRequired { + return installedVersion, nil + } + + } + return installedVersion, doInstallationStrategy(installStatus) +} diff --git a/v2/internal/ffenestri/windows/x64/WebView2Loader.dll b/v2/internal/ffenestri/windows/x64/WebView2Loader.dll new file mode 100644 index 000000000..869459eca Binary files /dev/null and b/v2/internal/ffenestri/windows/x64/WebView2Loader.dll differ diff --git a/v2/internal/ffenestri/windows/x64/x64.go b/v2/internal/ffenestri/windows/x64/x64.go new file mode 100644 index 000000000..8e4e2f31b --- /dev/null +++ b/v2/internal/ffenestri/windows/x64/x64.go @@ -0,0 +1,8 @@ +// +build windows + +package x64 + +import _ "embed" + +//go:embed WebView2Loader.dll +var WebView2Loader []byte diff --git a/v2/internal/ffenestri/windows_checkboxes.go b/v2/internal/ffenestri/windows_checkboxes.go new file mode 100644 index 000000000..e76dfa8ca --- /dev/null +++ b/v2/internal/ffenestri/windows_checkboxes.go @@ -0,0 +1,93 @@ +//+build windows + +package ffenestri + +import ( + "fmt" + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/menumanager" + "os" + "sync" + "text/tabwriter" +) + +/* --------------------------------------------------------------------------------- + +Checkbox Cache +-------------- +The checkbox cache keeps a list of IDs that are associated with the same checkbox menu item. +This can happen when a checkbox is used in an application menu and a tray menu, eg "start at login". +The cache is used to bulk toggle the menu items when one is clicked. + +*/ + +type CheckboxCache struct { + cache map[*menumanager.ProcessedMenu]map[wailsMenuItemID][]win32MenuItemID + mutex sync.RWMutex +} + +func NewCheckboxCache() *CheckboxCache { + return &CheckboxCache{ + cache: make(map[*menumanager.ProcessedMenu]map[wailsMenuItemID][]win32MenuItemID), + } +} + +func (c *CheckboxCache) Dump() { + // Start a new tabwriter + w := new(tabwriter.Writer) + w.Init(os.Stdout, 8, 8, 0, '\t', 0) + + println("---------------- Checkbox", c, "Dump ----------------") + for _, processedMenu := range c.cache { + println("Menu", processedMenu) + for wailsMenuItemID, win32menus := range processedMenu { + println(" WailsMenu: ", wailsMenuItemID) + menus := slicer.String() + for _, win32menu := range win32menus { + menus.Add(fmt.Sprintf("%v", win32menu)) + } + _, _ = fmt.Fprintf(w, "%s\t%s\n", wailsMenuItemID, menus.Join(", ")) + _ = w.Flush() + } + } +} + +func (c *CheckboxCache) addToCheckboxCache(menu *menumanager.ProcessedMenu, item wailsMenuItemID, menuID win32MenuItemID) { + + // Get map for menu + if c.cache[menu] == nil { + c.cache[menu] = make(map[wailsMenuItemID][]win32MenuItemID) + } + menuMap := c.cache[menu] + + // Ensure we have a slice + if menuMap[item] == nil { + menuMap[item] = []win32MenuItemID{} + } + + c.mutex.Lock() + menuMap[item] = append(menuMap[item], menuID) + c.mutex.Unlock() + +} + +func (c *CheckboxCache) removeMenuFromCheckboxCache(menu *menumanager.ProcessedMenu) { + c.mutex.Lock() + delete(c.cache, menu) + c.mutex.Unlock() +} + +// win32MenuIDsForWailsMenuID returns all win32menuids that are used for a wails menu item id across +// all menus +func (c *CheckboxCache) win32MenuIDsForWailsMenuID(item wailsMenuItemID) []win32MenuItemID { + c.mutex.Lock() + result := []win32MenuItemID{} + for _, menu := range c.cache { + ids := menu[item] + if ids != nil { + result = append(result, ids...) + } + } + c.mutex.Unlock() + return result +} diff --git a/v2/internal/ffenestri/windows_errorhandler_debug.go b/v2/internal/ffenestri/windows_errorhandler_debug.go new file mode 100644 index 000000000..5de8758b6 --- /dev/null +++ b/v2/internal/ffenestri/windows_errorhandler_debug.go @@ -0,0 +1,28 @@ +//+build windows,debug + +package ffenestri + +import ( + "fmt" + "github.com/ztrue/tracerr" + "runtime" + "strings" +) + +func wall(err error, inputs ...interface{}) error { + if err == nil { + return nil + } + pc, _, _, _ := runtime.Caller(1) + funcName := runtime.FuncForPC(pc).Name() + splitName := strings.Split(funcName, ".") + message := "[" + splitName[len(splitName)-1] + "]" + if len(inputs) > 0 { + params := []string{} + for _, param := range inputs { + params = append(params, fmt.Sprintf("%v", param)) + } + message += "(" + strings.Join(params, " ") + ")" + } + return tracerr.Errorf(message) +} diff --git a/v2/internal/ffenestri/windows_errorhandler_production.go b/v2/internal/ffenestri/windows_errorhandler_production.go new file mode 100644 index 000000000..20c9dc9d8 --- /dev/null +++ b/v2/internal/ffenestri/windows_errorhandler_production.go @@ -0,0 +1,47 @@ +// +build windows,!debug + +package ffenestri + +import "C" +import ( + "fmt" + "golang.org/x/sys/windows" + "log" + "os" + "runtime" + "strings" + "syscall" +) + +func wall(err error, inputs ...interface{}) error { + if err == nil { + return nil + } + pc, _, _, _ := runtime.Caller(1) + funcName := runtime.FuncForPC(pc).Name() + splitName := strings.Split(funcName, ".") + message := "[" + splitName[len(splitName)-1] + "]" + if len(inputs) > 0 { + params := []string{} + for _, param := range inputs { + params = append(params, fmt.Sprintf("%v", param)) + } + message += "(" + strings.Join(params, " ") + ")" + } + + title, err := syscall.UTF16PtrFromString("Fatal Error") + if err != nil { + log.Fatal(err) + } + + text, err := syscall.UTF16PtrFromString("There has been a fatal error. Details:\n" + message) + if err != nil { + log.Fatal(err) + } + + var flags uint32 = windows.MB_ICONERROR | windows.MB_OK + + _, err = windows.MessageBox(0, text, title, flags|windows.MB_SYSTEMMODAL) + os.Exit(1) + return err +} diff --git a/v2/internal/ffenestri/windows_menu.go b/v2/internal/ffenestri/windows_menu.go new file mode 100644 index 000000000..384ffb7fe --- /dev/null +++ b/v2/internal/ffenestri/windows_menu.go @@ -0,0 +1,232 @@ +//+build windows + +package ffenestri + +import ( + "fmt" + "github.com/wailsapp/wails/v2/internal/menumanager" + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/menu/keys" + "runtime" + "strings" +) + +//-------------------- Types ------------------------ + +type win32MenuItemID uint32 +type win32Menu uintptr +type win32Window uintptr +type wailsMenuItemID string // The internal menu ID + +type Menu struct { + wailsMenu *menumanager.WailsMenu + menu win32Menu + menuType menuType + + // A list of all checkbox and radio menuitems we + // create for this menu + checkboxes []win32MenuItemID + radioboxes []win32MenuItemID + initiallySelectedRadioItems []win32MenuItemID +} + +func createMenu(wailsMenu *menumanager.WailsMenu, menuType menuType) (*Menu, error) { + + mainMenu, err := createWin32Menu() + if err != nil { + return nil, err + } + + result := &Menu{ + wailsMenu: wailsMenu, + menu: mainMenu, + menuType: menuType, + } + + // Process top level menus + for _, toplevelmenu := range applicationMenu.Menu.Items { + err := result.processMenuItem(result.menu, toplevelmenu) + if err != nil { + return nil, err + } + } + + err = result.processRadioGroups() + if err != nil { + return nil, err + } + + return result, nil +} + +func (m *Menu) processMenuItem(parent win32Menu, menuItem *menumanager.ProcessedMenuItem) error { + + // Ignore hidden items + if menuItem.Hidden { + return nil + } + + // Calculate the flags for this menu item + flags := uintptr(calculateFlags(menuItem)) + + switch menuItem.Type { + case menu.SubmenuType: + submenu, err := createWin32PopupMenu() + if err != nil { + return err + } + for _, submenuItem := range menuItem.SubMenu.Items { + err = m.processMenuItem(submenu, submenuItem) + if err != nil { + return err + } + } + err = appendWin32MenuItem(parent, flags, uintptr(submenu), menuItem.Label) + if err != nil { + return err + } + case menu.TextType, menu.CheckboxType, menu.RadioType: + win32ID := addMenuCacheEntry(parent, m.menuType, menuItem, m.wailsMenu.Menu) + if menuItem.Accelerator != nil { + m.processAccelerator(menuItem) + } + label := menuItem.Label + //label := fmt.Sprintf("%s (%d)", menuItem.Label, win32ID) + err := appendWin32MenuItem(parent, flags, uintptr(win32ID), label) + if err != nil { + return err + } + if menuItem.Type == menu.CheckboxType { + // We need to maintain a list of this menu's checkboxes + m.checkboxes = append(m.checkboxes, win32ID) + globalCheckboxCache.addToCheckboxCache(m.wailsMenu.Menu, wailsMenuItemID(menuItem.ID), win32ID) + } + if menuItem.Type == menu.RadioType { + // We need to maintain a list of this menu's radioitems + m.radioboxes = append(m.radioboxes, win32ID) + globalRadioGroupMap.addRadioGroupMapping(m.wailsMenu.Menu, wailsMenuItemID(menuItem.ID), win32ID) + if menuItem.Checked { + m.initiallySelectedRadioItems = append(m.initiallySelectedRadioItems, win32ID) + } + } + case menu.SeparatorType: + err := appendWin32MenuItem(parent, flags, 0, "") + if err != nil { + return err + } + } + return nil +} + +func (m *Menu) processRadioGroups() error { + + for _, rg := range applicationMenu.RadioGroups { + startWailsMenuID := wailsMenuItemID(rg.Members[0]) + endWailsMenuID := wailsMenuItemID(rg.Members[len(rg.Members)-1]) + + startIDs := globalRadioGroupMap.getRadioGroupMapping(startWailsMenuID) + endIDs := globalRadioGroupMap.getRadioGroupMapping(endWailsMenuID) + + var radioGroupMaps = []*radioGroupStartEnd{} + for index := range startIDs { + startID := startIDs[index] + endID := endIDs[index] + thisRadioGroup := &radioGroupStartEnd{ + startID: startID, + endID: endID, + } + radioGroupMaps = append(radioGroupMaps, thisRadioGroup) + } + + // Set this for each member + for _, member := range rg.Members { + id := wailsMenuItemID(member) + globalRadioGroupCache.addToRadioGroupCache(m.wailsMenu.Menu, id, radioGroupMaps) + } + } + + // Enable all initially checked radio items + for _, win32MenuID := range m.initiallySelectedRadioItems { + menuItemDetails := getMenuCacheEntry(win32MenuID) + wailsMenuID := wailsMenuItemID(menuItemDetails.item.ID) + err := selectRadioItemFromWailsMenuID(wailsMenuID, win32MenuID) + if err != nil { + return err + } + } + + return nil +} + +func (m *Menu) Destroy() error { + + // Release the MenuIDs + releaseMenuIDsForProcessedMenu(m.wailsMenu.Menu) + + // Unload this menu's checkboxes from the cache + globalCheckboxCache.removeMenuFromCheckboxCache(m.wailsMenu.Menu) + + // Unload this menu's radio groups from the cache + globalRadioGroupCache.removeMenuFromRadioBoxCache(m.wailsMenu.Menu) + + globalRadioGroupMap.removeMenuFromRadioGroupMapping(m.wailsMenu.Menu) + + // Free up callbacks + resetCallbacks() + + // Delete menu + return destroyWin32Menu(m.menu) +} + +func (m *Menu) processAccelerator(menuitem *menumanager.ProcessedMenuItem) { + + // Add in shortcut to label if there is no "\t" override + if !strings.Contains(menuitem.Label, "\t") { + menuitem.Label += "\t" + keys.Stringify(menuitem.Accelerator, runtime.GOOS) + } + + // Calculate the modifier + var modifiers uint8 + for _, mod := range menuitem.Accelerator.Modifiers { + switch mod { + case keys.ControlKey, keys.CmdOrCtrlKey: + modifiers |= 1 + case keys.OptionOrAltKey: + modifiers |= 2 + case keys.ShiftKey: + modifiers |= 4 + //case keys.SuperKey: + // modifiers |= 8 + } + } + + var keycode = calculateKeycode(strings.ToLower(menuitem.Accelerator.Key)) + if keycode == 0 { + fmt.Printf("WARNING: Key '%s' is unsupported in windows. Cannot bind callback.", menuitem.Accelerator.Key) + return + } + addMenuCallback(keycode, modifiers, menuitem.ID, m.menuType) + +} + +var flagMap = map[menu.Type]uint32{ + menu.TextType: MF_STRING, + menu.SeparatorType: MF_SEPARATOR, + menu.SubmenuType: MF_STRING | MF_POPUP, + menu.CheckboxType: MF_STRING, + menu.RadioType: MF_STRING, +} + +func calculateFlags(menuItem *menumanager.ProcessedMenuItem) uint32 { + result := flagMap[menuItem.Type] + + if menuItem.Disabled { + result |= MF_DISABLED + } + + if menuItem.Type == menu.CheckboxType && menuItem.Checked { + result |= MF_CHECKED + } + + return result +} diff --git a/v2/internal/ffenestri/windows_menu_cache.go b/v2/internal/ffenestri/windows_menu_cache.go new file mode 100644 index 000000000..5b6762d9b --- /dev/null +++ b/v2/internal/ffenestri/windows_menu_cache.go @@ -0,0 +1,74 @@ +//+build windows + +package ffenestri + +import ( + "github.com/leaanthony/idgen" + "github.com/wailsapp/wails/v2/internal/menumanager" + "sync" +) + +/** + +MenuCache +--------- +When windows calls back to Go (when an item is clicked), we need to +be able to retrieve information about the menu item: + - The menu that the menuitem is part of (parent) + - The original processed menu item + - The type of the menu (application, context or tray) + +This cache is built up when a menu is created. + +*/ + +// TODO: Make this like the other caches + +type menuCacheEntry struct { + parent win32Menu + menuType menuType + item *menumanager.ProcessedMenuItem + processedMenu *menumanager.ProcessedMenu +} + +var idGenerator = idgen.New() + +var menuCache = map[win32MenuItemID]*menuCacheEntry{} +var menuCacheLock sync.RWMutex +var wailsMenuIDtoWin32IDMap = map[wailsMenuItemID]win32MenuItemID{} + +// This releases the menuIDs back to the id generator +var winIDsOwnedByProcessedMenu = map[*menumanager.ProcessedMenu][]win32MenuItemID{} + +func releaseMenuIDsForProcessedMenu(processedMenu *menumanager.ProcessedMenu) { + for _, menuID := range winIDsOwnedByProcessedMenu[processedMenu] { + idGenerator.ReleaseID(uint(menuID)) + } + delete(winIDsOwnedByProcessedMenu, processedMenu) +} + +func addMenuCacheEntry(parent win32Menu, typ menuType, wailsMenuItem *menumanager.ProcessedMenuItem, processedMenu *menumanager.ProcessedMenu) win32MenuItemID { + menuCacheLock.Lock() + defer menuCacheLock.Unlock() + id, err := idGenerator.NewID() + checkFatal(err) + menuID := win32MenuItemID(id) + menuCache[menuID] = &menuCacheEntry{ + parent: parent, + menuType: typ, + item: wailsMenuItem, + processedMenu: processedMenu, + } + // save the mapping + wailsMenuIDtoWin32IDMap[wailsMenuItemID(wailsMenuItem.ID)] = menuID + // keep track of menuids owned by this menu (so we can release the ids) + winIDsOwnedByProcessedMenu[processedMenu] = append(winIDsOwnedByProcessedMenu[processedMenu], menuID) + return menuID + +} + +func getMenuCacheEntry(id win32MenuItemID) *menuCacheEntry { + menuCacheLock.Lock() + defer menuCacheLock.Unlock() + return menuCache[id] +} diff --git a/v2/internal/ffenestri/windows_menu_callbacks.go b/v2/internal/ffenestri/windows_menu_callbacks.go new file mode 100644 index 000000000..9bfae895b --- /dev/null +++ b/v2/internal/ffenestri/windows_menu_callbacks.go @@ -0,0 +1,126 @@ +package ffenestri + +type callbackData struct { + menuID string + menuType menuType +} + +var callbacks = map[uint16]map[uint8]callbackData{} + +func addMenuCallback(key uint16, modifiers uint8, menuID string, menutype menuType) { + + if callbacks[key] == nil { + callbacks[key] = make(map[uint8]callbackData) + } + callbacks[key][modifiers] = callbackData{ + menuID: menuID, + menuType: menutype, + } +} + +func resetCallbacks() { + callbacks = map[uint16]map[uint8]callbackData{} +} + +func getCallbackForKeyPress(key uint16, modifiers uint8) (string, menuType) { + if callbacks[key] == nil { + return "", "" + } + result := callbacks[key][modifiers] + return result.menuID, result.menuType +} + +func calculateKeycode(key string) uint16 { + return keymap[key] +} + +// TODO: Complete this list +var keymap = map[string]uint16{ + "0": 0x30, + "1": 0x31, + "2": 0x32, + "3": 0x33, + "4": 0x34, + "5": 0x35, + "6": 0x36, + "7": 0x37, + "8": 0x38, + "9": 0x39, + "a": 0x41, + "b": 0x42, + "c": 0x43, + "d": 0x44, + "e": 0x45, + "f": 0x46, + "g": 0x47, + "h": 0x48, + "i": 0x49, + "j": 0x4A, + "k": 0x4B, + "l": 0x4C, + "m": 0x4D, + "n": 0x4E, + "o": 0x4F, + "p": 0x50, + "q": 0x51, + "r": 0x52, + "s": 0x53, + "t": 0x54, + "u": 0x55, + "v": 0x56, + "w": 0x57, + "x": 0x58, + "y": 0x59, + "z": 0x5A, + "backspace": 0x08, + "tab": 0x09, + "return": 0x0D, + "enter": 0x0D, + "escape": 0x1B, + "left": 0x25, + "right": 0x27, + "up": 0x26, + "down": 0x28, + "space": 0x20, + "delete": 0x2E, + "home": 0x24, + "end": 0x23, + "page up": 0x21, + "page down": 0x22, + "f1": 0x70, + "f2": 0x71, + "f3": 0x72, + "f4": 0x73, + "f5": 0x74, + "f6": 0x75, + "f7": 0x76, + "f8": 0x77, + "f9": 0x78, + "f10": 0x79, + "f11": 0x7A, + "f12": 0x7B, + "f13": 0x7C, + "f14": 0x7D, + "f15": 0x7E, + "f16": 0x7F, + "f17": 0x80, + "f18": 0x81, + "f19": 0x82, + "f20": 0x83, + "f21": 0x84, + "f22": 0x85, + "f23": 0x86, + "f24": 0x87, + // Windows doesn't have these apparently so use 0 for unsupported + "f25": 0, + "f26": 0, + "f27": 0, + "f28": 0, + "f29": 0, + "f30": 0, + "f31": 0, + "f32": 0, + "f33": 0, + "f34": 0, + "f35": 0, +} diff --git a/v2/internal/ffenestri/windows_radiogroup.go b/v2/internal/ffenestri/windows_radiogroup.go new file mode 100644 index 000000000..3f3d349ff --- /dev/null +++ b/v2/internal/ffenestri/windows_radiogroup.go @@ -0,0 +1,194 @@ +//+build windows + +package ffenestri + +import ( + "fmt" + "github.com/leaanthony/slicer" + "os" + "sync" + "text/tabwriter" + + "github.com/wailsapp/wails/v2/internal/menumanager" +) + +/* --------------------------------------------------------------------------------- + +Radio Groups +------------ +Radio groups are stored by the ProcessedMenu as a list of menu ids. +Windows only cares about the start and end ids of the group so we +preprocess the radio groups and store this data in a radioGroupMap. +When a radio button is clicked, we use the menu id to read in the +radio group data and call CheckMenuRadioItem to update the group. + +*/ + +type radioGroupStartEnd struct { + startID win32MenuItemID + endID win32MenuItemID +} + +type RadioGroupCache struct { + cache map[*menumanager.ProcessedMenu]map[wailsMenuItemID][]*radioGroupStartEnd + mutex sync.RWMutex +} + +func NewRadioGroupCache() *RadioGroupCache { + return &RadioGroupCache{ + cache: make(map[*menumanager.ProcessedMenu]map[wailsMenuItemID][]*radioGroupStartEnd), + } +} + +func (c *RadioGroupCache) Dump() { + // Start a new tabwriter + w := new(tabwriter.Writer) + w.Init(os.Stdout, 8, 8, 0, '\t', 0) + + println("---------------- RadioGroupCache", c, "Dump ----------------") + for menu, processedMenu := range c.cache { + println("Menu", menu) + _, _ = fmt.Fprintf(w, "Wails ID \tWindows ID Pairs\n") + for wailsMenuItemID, radioGroupStartEnd := range processedMenu { + menus := slicer.String() + for _, se := range radioGroupStartEnd { + menus.Add(fmt.Sprintf("[%d -> %d]", se.startID, se.endID)) + } + _, _ = fmt.Fprintf(w, "%s\t%s\n", wailsMenuItemID, menus.Join(", ")) + _ = w.Flush() + } + } +} + +func (c *RadioGroupCache) addToRadioGroupCache(menu *menumanager.ProcessedMenu, item wailsMenuItemID, radioGroupMaps []*radioGroupStartEnd) { + + c.mutex.Lock() + + // Get map for menu + if c.cache[menu] == nil { + c.cache[menu] = make(map[wailsMenuItemID][]*radioGroupStartEnd) + } + menuMap := c.cache[menu] + + // Ensure we have a slice + if menuMap[item] == nil { + menuMap[item] = []*radioGroupStartEnd{} + } + + menuMap[item] = radioGroupMaps + + c.mutex.Unlock() + +} + +func (c *RadioGroupCache) removeMenuFromRadioBoxCache(menu *menumanager.ProcessedMenu) { + c.mutex.Lock() + delete(c.cache, menu) + c.mutex.Unlock() +} + +func (c *RadioGroupCache) getRadioGroupMappings(wailsMenuID wailsMenuItemID) []*radioGroupStartEnd { + c.mutex.Lock() + result := []*radioGroupStartEnd{} + for _, menugroups := range c.cache { + groups := menugroups[wailsMenuID] + if groups != nil { + result = append(result, groups...) + } + } + c.mutex.Unlock() + return result +} + +type RadioGroupMap struct { + cache map[*menumanager.ProcessedMenu]map[wailsMenuItemID][]win32MenuItemID + mutex sync.RWMutex +} + +func NewRadioGroupMap() *RadioGroupMap { + return &RadioGroupMap{ + cache: make(map[*menumanager.ProcessedMenu]map[wailsMenuItemID][]win32MenuItemID), + } +} + +func (c *RadioGroupMap) Dump() { + // Start a new tabwriter + w := new(tabwriter.Writer) + w.Init(os.Stdout, 8, 8, 0, '\t', 0) + + println("---------------- RadioGroupMap", c, "Dump ----------------") + for _, processedMenu := range c.cache { + _, _ = fmt.Fprintf(w, "Menu\tWails ID \tWindows IDs\n") + for wailsMenuItemID, win32menus := range processedMenu { + menus := slicer.String() + for _, win32menu := range win32menus { + menus.Add(fmt.Sprintf("%v", win32menu)) + } + _, _ = fmt.Fprintf(w, "%p\t%s\t%s\n", processedMenu, wailsMenuItemID, menus.Join(", ")) + _ = w.Flush() + } + } +} + +func (m *RadioGroupMap) addRadioGroupMapping(menu *menumanager.ProcessedMenu, item wailsMenuItemID, win32ID win32MenuItemID) { + m.mutex.Lock() + + // Get map for menu + if m.cache[menu] == nil { + m.cache[menu] = make(map[wailsMenuItemID][]win32MenuItemID) + } + menuMap := m.cache[menu] + + // Ensure we have a slice + if menuMap[item] == nil { + menuMap[item] = []win32MenuItemID{} + } + + menuMap[item] = append(menuMap[item], win32ID) + + m.mutex.Unlock() +} + +func (m *RadioGroupMap) removeMenuFromRadioGroupMapping(menu *menumanager.ProcessedMenu) { + m.mutex.Lock() + delete(m.cache, menu) + m.mutex.Unlock() +} + +func (m *RadioGroupMap) getRadioGroupMapping(wailsMenuID wailsMenuItemID) []win32MenuItemID { + m.mutex.Lock() + result := []win32MenuItemID{} + for _, menuids := range m.cache { + ids := menuids[wailsMenuID] + if ids != nil { + result = append(result, ids...) + } + } + m.mutex.Unlock() + return result +} + +func selectRadioItemFromWailsMenuID(wailsMenuID wailsMenuItemID, win32MenuID win32MenuItemID) error { + radioItemGroups := globalRadioGroupCache.getRadioGroupMappings(wailsMenuID) + // Figure out offset into group + var offset win32MenuItemID = 0 + for _, radioItemGroup := range radioItemGroups { + if win32MenuID >= radioItemGroup.startID && win32MenuID <= radioItemGroup.endID { + offset = win32MenuID - radioItemGroup.startID + break + } + } + for _, radioItemGroup := range radioItemGroups { + selectedMenuID := radioItemGroup.startID + offset + menuItemDetails := getMenuCacheEntry(selectedMenuID) + if menuItemDetails != nil { + if menuItemDetails.parent != 0 { + err := selectRadioItem(selectedMenuID, radioItemGroup.startID, radioItemGroup.endID, menuItemDetails.parent) + if err != nil { + return err + } + } + } + } + return nil +} diff --git a/v2/internal/ffenestri/windows_win32.go b/v2/internal/ffenestri/windows_win32.go new file mode 100644 index 000000000..5942b9bcf --- /dev/null +++ b/v2/internal/ffenestri/windows_win32.go @@ -0,0 +1,130 @@ +//+build windows + +package ffenestri + +import ( + "unsafe" + + "github.com/wailsapp/wails/v2/internal/menumanager" + "golang.org/x/sys/windows" +) + +var ( + // DLL stuff + user32 = windows.NewLazySystemDLL("User32.dll") + win32CreateMenu = user32.NewProc("CreateMenu") + win32DestroyMenu = user32.NewProc("DestroyMenu") + win32CreatePopupMenu = user32.NewProc("CreatePopupMenu") + win32AppendMenuW = user32.NewProc("AppendMenuW") + win32SetMenu = user32.NewProc("SetMenu") + win32CheckMenuItem = user32.NewProc("CheckMenuItem") + win32GetMenuState = user32.NewProc("GetMenuState") + win32CheckMenuRadioItem = user32.NewProc("CheckMenuRadioItem") + + applicationMenu *menumanager.WailsMenu + menuManager *menumanager.Manager +) + +const MF_BITMAP uint32 = 0x00000004 +const MF_CHECKED uint32 = 0x00000008 +const MF_DISABLED uint32 = 0x00000002 +const MF_ENABLED uint32 = 0x00000000 +const MF_GRAYED uint32 = 0x00000001 +const MF_MENUBARBREAK uint32 = 0x00000020 +const MF_MENUBREAK uint32 = 0x00000040 +const MF_OWNERDRAW uint32 = 0x00000100 +const MF_POPUP uint32 = 0x00000010 +const MF_SEPARATOR uint32 = 0x00000800 +const MF_STRING uint32 = 0x00000000 +const MF_UNCHECKED uint32 = 0x00000000 +const MF_BYCOMMAND uint32 = 0x00000000 +const MF_BYPOSITION uint32 = 0x00000400 + +const WM_SIZE = 5 +const WM_GETMINMAXINFO = 36 + +type Win32Rect struct { + Left int32 + Top int32 + Right int32 + Bottom int32 +} + +// ------------------- win32 calls ----------------------- + +func createWin32Menu() (win32Menu, error) { + res, _, err := win32CreateMenu.Call() + if res == 0 { + return 0, wall(err) + } + return win32Menu(res), nil +} + +func destroyWin32Menu(menu win32Menu) error { + res, _, err := win32DestroyMenu.Call(uintptr(menu)) + if res == 0 { + return wall(err, "Menu:", menu) + } + return nil +} + +func createWin32PopupMenu() (win32Menu, error) { + res, _, err := win32CreatePopupMenu.Call() + if res == 0 { + return 0, wall(err) + } + return win32Menu(res), nil +} + +func appendWin32MenuItem(menu win32Menu, flags uintptr, submenuOrID uintptr, label string) error { + menuText, err := windows.UTF16PtrFromString(label) + if err != nil { + return err + } + res, _, err := win32AppendMenuW.Call( + uintptr(menu), + flags, + submenuOrID, + uintptr(unsafe.Pointer(menuText)), + ) + if res == 0 { + return wall(err, "Menu", menu, "Flags", flags, "submenuOrID", submenuOrID, "label", label) + } + return nil +} + +func setWindowMenu(window win32Window, menu win32Menu) error { + res, _, err := win32SetMenu.Call(uintptr(window), uintptr(menu)) + if res == 0 { + return wall(err, "window", window, "menu", menu) + } + return nil +} + +func selectRadioItem(selectedMenuID, startMenuItemID, endMenuItemID win32MenuItemID, parent win32Menu) error { + res, _, err := win32CheckMenuRadioItem.Call(uintptr(parent), uintptr(startMenuItemID), uintptr(endMenuItemID), uintptr(selectedMenuID), uintptr(MF_BYCOMMAND)) + if int(res) == 0 { + return wall(err, selectedMenuID, startMenuItemID, endMenuItemID, parent) + } + return nil +} + +// +//func getWindowRect(window win32Window) (*Win32Rect, error) { +// var windowRect Win32Rect +// res, _, err := win32GetWindowRect.Call(uintptr(window), uintptr(unsafe.Pointer(&windowRect))) +// if res == 0 { +// return nil, err +// } +// return &windowRect, nil +//} +// +//func getClientRect(window win32Window) (*Win32Rect, error) { +// var clientRect Win32Rect +// res, _, err := win32GetClientRect.Call(uintptr(window), uintptr(unsafe.Pointer(&clientRect))) +// if res == 0 { +// return nil, err +// } +// return &clientRect, nil +//} +// diff --git a/v2/internal/ffenestri/wv2ComHandler_windows.h b/v2/internal/ffenestri/wv2ComHandler_windows.h new file mode 100644 index 000000000..60fc43574 --- /dev/null +++ b/v2/internal/ffenestri/wv2ComHandler_windows.h @@ -0,0 +1,126 @@ + +#ifndef WV2COMHANDLER_H +#define WV2COMHANDLER_H + +#include "ffenestri_windows.h" +#include "windows/WebView2.h" + +#include +#include + +class wv2ComHandler + : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler, + public ICoreWebView2CreateCoreWebView2ControllerCompletedHandler, + public ICoreWebView2WebMessageReceivedEventHandler, + public ICoreWebView2PermissionRequestedEventHandler, + public ICoreWebView2AcceleratorKeyPressedEventHandler +{ + + struct Application *app; + HWND window; + messageCallback mcb; + comHandlerCallback cb; + + public: + wv2ComHandler(struct Application *app, HWND window, messageCallback mcb, comHandlerCallback cb) { + this->app = app; + this->window = window; + this->mcb = mcb; + this->cb = cb; + } + ULONG STDMETHODCALLTYPE AddRef() { return 1; } + ULONG STDMETHODCALLTYPE Release() { return 1; } + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppv) { + return S_OK; + } + HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, + ICoreWebView2Environment *env) { + env->CreateCoreWebView2Controller(window, this); + return S_OK; + } + HRESULT STDMETHODCALLTYPE Invoke(HRESULT res, + ICoreWebView2Controller *controller) { + controller->AddRef(); + + ICoreWebView2 *webview; + ::EventRegistrationToken token; + controller->get_CoreWebView2(&webview); + controller->add_AcceleratorKeyPressed(this, &token); + webview->add_WebMessageReceived(this, &token); + webview->add_PermissionRequested(this, &token); + + cb(controller); + return S_OK; + } + + // This is our keyboard callback method + HRESULT STDMETHODCALLTYPE Invoke(ICoreWebView2Controller *controller, ICoreWebView2AcceleratorKeyPressedEventArgs * args) { + // Prevent WebView2 from processing the key + args->put_Handled(TRUE); + + COREWEBVIEW2_KEY_EVENT_KIND kind; + args->get_KeyEventKind(&kind); + if (kind == COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN || + kind == COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN) + { + UINT key; + args->get_VirtualKey(&key); + COREWEBVIEW2_PHYSICAL_KEY_STATUS status; + args->get_PhysicalKeyStatus(&status); + if (!status.WasKeyDown) + { + processKeyPress(key); + } + } + return S_OK; + } + + // This is called when JS posts a message back to webkit + HRESULT STDMETHODCALLTYPE Invoke( + ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) { + LPWSTR message; + args->TryGetWebMessageAsString(&message); + if ( message == nullptr ) { + return S_OK; + } + const char *m = LPWSTRToCstr(message); + + // check for internal messages + if (strcmp(m, "completed") == 0) { + completed(app); + return S_OK; + } + else if (strcmp(m, "initialised") == 0) { + loadAssets(app); + return S_OK; + } + else if (strcmp(m, "wails-drag") == 0) { + // We don't drag in fullscreen mode + if (!app->isFullscreen) { + ReleaseCapture(); + SendMessage(this->window, WM_NCLBUTTONDOWN, HTCAPTION, 0); + } + return S_OK; + } + else { + messageFromWindowCallback(m); + } + delete[] m; + return S_OK; + } + HRESULT STDMETHODCALLTYPE + Invoke(ICoreWebView2 *sender, + ICoreWebView2PermissionRequestedEventArgs *args) { + printf("DDDDDDDDDDDD\n"); + + COREWEBVIEW2_PERMISSION_KIND kind; + args->get_PermissionKind(&kind); + if (kind == COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ) { + args->put_State(COREWEBVIEW2_PERMISSION_STATE_ALLOW); + } + return S_OK; + } + +}; + +#endif \ No newline at end of file diff --git a/v2/internal/frontend/assetserver/assetserver_browser_dev.go b/v2/internal/frontend/assetserver/assetserver_browser_dev.go new file mode 100644 index 000000000..dc1bfb15e --- /dev/null +++ b/v2/internal/frontend/assetserver/assetserver_browser_dev.go @@ -0,0 +1,115 @@ +//go:build dev +// +build dev + +package assetserver + +import ( + "bytes" + "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/wailsapp/wails/v2/pkg/options" + "golang.org/x/net/html" + "path/filepath" + "strings" +) + +/* + +The assetserver for dev serves assets from disk. +It injects a websocket based IPC script into `index.html`. + +*/ + +import ( + "os" +) + +type BrowserAssetServer struct { + runtimeJS []byte + assetdir string + appOptions *options.App +} + +func NewBrowserAssetServer(assetdir string, bindingsJSON string, appOptions *options.App) (*BrowserAssetServer, error) { + result := &BrowserAssetServer{ + assetdir: assetdir, + appOptions: appOptions, + } + + var buffer bytes.Buffer + buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n") + buffer.Write(runtime.RuntimeDesktopJS) + result.runtimeJS = buffer.Bytes() + return result, nil +} + +func (a *BrowserAssetServer) loadFileFromDisk(filename string) ([]byte, error) { + return os.ReadFile(filepath.Join(a.assetdir, filename)) +} + +func (a *BrowserAssetServer) processIndexHTML() ([]byte, error) { + indexHTML, err := a.loadFileFromDisk("index.html") + if err != nil { + return nil, err + } + htmlNode, err := getHTMLNode(indexHTML) + if err != nil { + return nil, err + } + err = appendSpinnerToBody(htmlNode) + if err != nil { + return nil, err + } + wailsOptions, err := extractOptions(indexHTML) + if err != nil { + return nil, err + } + + if wailsOptions.disableIPCInjection == false { + err := insertScriptInHead(htmlNode, "/wails/ipc.js") + if err != nil { + return nil, err + } + } + + if wailsOptions.disableRuntimeInjection == false { + err := insertScriptInHead(htmlNode, "/wails/runtime.js") + if err != nil { + return nil, err + } + } + + var buffer bytes.Buffer + err = html.Render(&buffer, htmlNode) + if err != nil { + return nil, err + } + return buffer.Bytes(), nil +} + +func (a *BrowserAssetServer) Load(filename string) ([]byte, string, error) { + var content []byte + var err error + switch filename { + case "/": + content, err = a.processIndexHTML() + case "/wails/runtime.js": + content = a.runtimeJS + case "/wails/ipc.js": + content = runtime.WebsocketIPC + default: + content, err = a.loadFileFromDisk(filename) + if strings.HasSuffix(filename, ".js") { + var buffer bytes.Buffer + buffer.WriteString("window.awaitIPC('" + filename + "', ()=>{") + buffer.Write(content) + buffer.WriteString(` +});`) + content = buffer.Bytes() + } + } + if err != nil { + return nil, "", err + } + mimeType := GetMimetype(filename, content) + return content, mimeType, nil +} diff --git a/v2/internal/frontend/assetserver/assetserver_desktop.go b/v2/internal/frontend/assetserver/assetserver_desktop.go new file mode 100644 index 000000000..1ec4a7532 --- /dev/null +++ b/v2/internal/frontend/assetserver/assetserver_desktop.go @@ -0,0 +1,156 @@ +package assetserver + +import ( + "bytes" + "context" + "embed" + "fmt" + "github.com/leaanthony/debme" + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/wailsapp/wails/v2/internal/logger" + "io/fs" + "log" + "path/filepath" + "strings" +) + +type DesktopAssetServer struct { + assets debme.Debme + runtimeJS []byte + assetdir string + logger *logger.Logger +} + +func NewDesktopAssetServer(ctx context.Context, assets embed.FS, bindingsJSON string) (*DesktopAssetServer, error) { + result := &DesktopAssetServer{} + + _logger := ctx.Value("logger") + if _logger != nil { + result.logger = _logger.(*logger.Logger) + } + + _assetdir := ctx.Value("assetdir") + if _assetdir != nil { + result.assetdir = _assetdir.(string) + absdir, err := filepath.Abs(result.assetdir) + if err != nil { + return nil, err + } + result.LogDebug("Loading assets from: %s", absdir) + } + + var buffer bytes.Buffer + buffer.WriteString(`window.wailsbindings='` + bindingsJSON + `';` + "\n") + buffer.Write(runtime.RuntimeDesktopJS) + result.runtimeJS = buffer.Bytes() + err := result.init(assets) + return result, err +} + +func (d *DesktopAssetServer) LogDebug(message string, args ...interface{}) { + if d.logger != nil { + d.logger.Debug("[DesktopAssetServer] "+message, args...) + } +} + +func (d *DesktopAssetServer) SetAssetDir(assetdir string) { + d.assetdir = assetdir +} + +func PathToIndexHTML(assets embed.FS) (string, error) { + stat, err := fs.Stat(assets, "index.html") + if stat != nil { + return ".", nil + } + var indexFiles slicer.StringSlicer + err = fs.WalkDir(assets, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if strings.HasSuffix(path, "index.html") { + indexFiles.Add(path) + } + return nil + }) + if err != nil { + return "", err + } + + if indexFiles.Length() > 1 { + return "", fmt.Errorf("multiple 'index.html' files found in assets") + } + + path, _ := filepath.Split(indexFiles.AsSlice()[0]) + return path, nil +} + +func processAssets(assets embed.FS) (debme.Debme, error) { + + result, err := debme.FS(assets, ".") + if err != nil { + return result, err + } + // Find index.html + path, err := PathToIndexHTML(assets) + if err != nil { + return debme.Debme{}, err + } + return debme.FS(assets, path) +} + +func (a *DesktopAssetServer) init(assets embed.FS) error { + + var err error + a.assets, err = processAssets(assets) + if err != nil { + return err + } + return nil +} + +func (a *DesktopAssetServer) processIndexHTML() ([]byte, error) { + indexHTML, err := a.ReadFile("index.html") + if err != nil { + return nil, err + } + wailsOptions, err := extractOptions(indexHTML) + if err != nil { + log.Fatal(err) + return nil, err + } + if wailsOptions.disableRuntimeInjection == false { + indexHTML, err = injectHTML(string(indexHTML), ``) + if err != nil { + return nil, err + } + } + if wailsOptions.disableIPCInjection == false { + indexHTML, err = injectHTML(string(indexHTML), ``) + if err != nil { + return nil, err + } + } + + return indexHTML, nil +} + +func (a *DesktopAssetServer) Load(filename string) ([]byte, string, error) { + var content []byte + var err error + switch filename { + case "/": + content, err = a.processIndexHTML() + case "/wails/runtime.js": + content = a.runtimeJS + case "/wails/ipc.js": + content = runtime.DesktopIPC + default: + content, err = a.ReadFile(filename) + } + if err != nil { + return nil, "", err + } + mimeType := GetMimetype(filename, content) + return content, mimeType, nil +} diff --git a/v2/internal/frontend/assetserver/assetserver_desktop_dev.go b/v2/internal/frontend/assetserver/assetserver_desktop_dev.go new file mode 100644 index 000000000..42726ae3a --- /dev/null +++ b/v2/internal/frontend/assetserver/assetserver_desktop_dev.go @@ -0,0 +1,13 @@ +//go:build dev + +package assetserver + +import ( + "os" + "path/filepath" +) + +func (a *DesktopAssetServer) ReadFile(filename string) ([]byte, error) { + a.LogDebug("Loading file from disk: %s", filename) + return os.ReadFile(filepath.Join(a.assetdir, filename)) +} diff --git a/v2/internal/frontend/assetserver/assetserver_desktop_production.go b/v2/internal/frontend/assetserver/assetserver_desktop_production.go new file mode 100644 index 000000000..fb09ee1a3 --- /dev/null +++ b/v2/internal/frontend/assetserver/assetserver_desktop_production.go @@ -0,0 +1,7 @@ +//go:build production + +package assetserver + +func (a *DesktopAssetServer) ReadFile(filename string) ([]byte, error) { + return a.assets.ReadFile(filename) +} diff --git a/v2/internal/frontend/assetserver/common.go b/v2/internal/frontend/assetserver/common.go new file mode 100644 index 000000000..0695d7f27 --- /dev/null +++ b/v2/internal/frontend/assetserver/common.go @@ -0,0 +1,163 @@ +package assetserver + +import ( + "bytes" + "errors" + "fmt" + "golang.org/x/net/html" + "strings" +) + +type optionType string + +const ( + noAutoInject optionType = "noautoinject" + noAutoInjectRuntime optionType = "noautoinjectruntime" + noAutoInjectIPC optionType = "noautoinjectipc" +) + +type Options struct { + disableRuntimeInjection bool + disableIPCInjection bool +} + +func newOptions(optionString string) *Options { + var result = &Options{} + optionString = strings.ToLower(optionString) + options := strings.Split(optionString, ",") + for _, option := range options { + switch optionType(strings.TrimSpace(option)) { + case noAutoInject: + result.disableRuntimeInjection = true + result.disableIPCInjection = true + case noAutoInjectIPC: + result.disableIPCInjection = true + case noAutoInjectRuntime: + result.disableRuntimeInjection = true + } + } + return result +} + +func injectHTML(input string, html string) ([]byte, error) { + splits := strings.Split(input, "") + if len(splits) != 2 { + return nil, fmt.Errorf("unable to locate a tag in your html") + } + + var result bytes.Buffer + result.WriteString(splits[0]) + result.WriteString(html) + result.WriteString("") + result.WriteString(splits[1]) + return result.Bytes(), nil +} + +func extractOptions(htmldata []byte) (*Options, error) { + doc, err := html.Parse(bytes.NewReader(htmldata)) + if err != nil { + return nil, err + } + var extractor func(*html.Node) *Options + extractor = func(node *html.Node) *Options { + if node.Type == html.ElementNode && node.Data == "meta" { + isWailsOptionsTag := false + wailsOptions := "" + for _, attr := range node.Attr { + if isWailsOptionsTag && attr.Key == "content" { + wailsOptions = attr.Val + } + if attr.Val == "wails-options" { + isWailsOptionsTag = true + } + } + return newOptions(wailsOptions) + } + for child := node.FirstChild; child != nil; child = child.NextSibling { + result := extractor(child) + if result != nil { + return result + } + } + return nil + } + result := extractor(doc) + if result == nil { + result = &Options{} + } + return result, nil +} + +func createScriptNode(scriptName string) *html.Node { + return &html.Node{ + Type: html.ElementNode, + Data: "script", + Attr: []html.Attribute{ + { + Key: "src", + Val: scriptName, + }, + }, + } +} + +func createDivNode(id string) *html.Node { + return &html.Node{ + Type: html.ElementNode, + Data: "div", + Attr: []html.Attribute{ + { + Namespace: "", + Key: "id", + Val: id, + }, + }, + } +} + +func insertScriptInHead(htmlNode *html.Node, scriptName string) error { + headNode := findFirstTag(htmlNode, "head") + if headNode == nil { + return errors.New("cannot find head in HTML") + } + scriptNode := createScriptNode(scriptName) + if headNode.FirstChild != nil { + headNode.InsertBefore(scriptNode, headNode.FirstChild) + } else { + headNode.AppendChild(scriptNode) + } + return nil +} + +func appendSpinnerToBody(htmlNode *html.Node) error { + bodyNode := findFirstTag(htmlNode, "body") + if bodyNode == nil { + return errors.New("cannot find body in HTML") + } + scriptNode := createDivNode("wails-spinner") + bodyNode.AppendChild(scriptNode) + return nil +} + +func getHTMLNode(htmldata []byte) (*html.Node, error) { + return html.Parse(bytes.NewReader(htmldata)) +} + +func findFirstTag(htmlnode *html.Node, tagName string) *html.Node { + var extractor func(*html.Node) *html.Node + var result *html.Node + extractor = func(node *html.Node) *html.Node { + if node.Type == html.ElementNode && node.Data == tagName { + return node + } + for child := node.FirstChild; child != nil; child = child.NextSibling { + result := extractor(child) + if result != nil { + return result + } + } + return nil + } + result = extractor(htmlnode) + return result +} diff --git a/v2/internal/frontend/assetserver/common_test.go b/v2/internal/frontend/assetserver/common_test.go new file mode 100644 index 000000000..a60e1e12f --- /dev/null +++ b/v2/internal/frontend/assetserver/common_test.go @@ -0,0 +1,70 @@ +package assetserver + +import ( + "reflect" + "testing" +) + +const realHTML = ` + + + test3 + + + + + + +
Please enter your name below �
+
+ + +
+ + + + + +` + +func genMeta(content string) []byte { + return []byte("") +} + +func genOptions(runtime bool, bindings bool) *Options { + return &Options{ + disableRuntimeInjection: runtime, + disableIPCInjection: bindings, + } +} + +func Test_extractOptions(t *testing.T) { + tests := []struct { + name string + htmldata []byte + want *Options + wantError bool + }{ + {"empty", []byte(""), &Options{}, false}, + {"bad data", []byte("<"), &Options{}, false}, + {"bad options", genMeta("noauto"), genOptions(false, false), false}, + {"realhtml", []byte(realHTML), genOptions(true, true), false}, + {"noautoinject", genMeta("noautoinject"), genOptions(true, true), false}, + {"noautoinjectipc", genMeta("noautoinjectipc"), genOptions(false, true), false}, + {"noautoinjectruntime", genMeta("noautoinjectruntime"), genOptions(true, false), false}, + {"spaces", genMeta(" noautoinjectruntime "), genOptions(true, false), false}, + {"multiple", genMeta("noautoinjectruntime,noautoinjectipc"), genOptions(true, true), false}, + {"multiple spaces", genMeta(" noautoinjectruntime, noautoinjectipc "), genOptions(true, true), false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := extractOptions(tt.htmldata) + if !tt.wantError && err != nil { + t.Errorf("did not want error but got it") + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("extractOptions() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/v2/internal/frontend/assetserver/mimecache.go b/v2/internal/frontend/assetserver/mimecache.go new file mode 100644 index 000000000..ae33f7a54 --- /dev/null +++ b/v2/internal/frontend/assetserver/mimecache.go @@ -0,0 +1,47 @@ +package assetserver + +import ( + "net/http" + "path/filepath" + "strings" + "sync" + + "github.com/gabriel-vasile/mimetype" +) + +var ( + cache = map[string]string{} + mutex sync.Mutex +) + +func GetMimetype(filename string, data []byte) string { + mutex.Lock() + defer mutex.Unlock() + + result := cache[filename] + if result != "" { + return result + } + + detect := mimetype.Detect(data) + if detect == nil { + result = http.DetectContentType(data) + } else { + result = detect.String() + } + + if filepath.Ext(filename) == ".css" && strings.HasPrefix(result, "text/plain") { + result = strings.Replace(result, "text/plain", "text/css", 1) + } + + if filepath.Ext(filename) == ".js" && strings.HasPrefix(result, "text/plain") { + result = strings.Replace(result, "text/plain", "text/javascript", 1) + } + + if result == "" { + result = "application/octet-stream" + } + + cache[filename] = result + return result +} diff --git a/v2/internal/frontend/assetserver/mimecache_test.go b/v2/internal/frontend/assetserver/mimecache_test.go new file mode 100644 index 000000000..d6dbb9dfc --- /dev/null +++ b/v2/internal/frontend/assetserver/mimecache_test.go @@ -0,0 +1,26 @@ +package assetserver + +import "testing" + +func TestGetMimetype(t *testing.T) { + type args struct { + filename string + data []byte + } + tests := []struct { + name string + args args + want string + }{ + // TODO: Add test cases. + {"css", args{"test.css", []byte("body{margin:0;padding:0;background-color:#d579b2}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;background-color:#ededed}#nav{padding:30px}#nav a{font-weight:700;color:#2c\n3e50}#nav a.router-link-exact-active{color:#42b983}.hello[data-v-4e26ad49]{margin:10px 0}")}, "text/css; charset=utf-8"}, + {"js", args{"test.js", []byte("let foo = 'bar'; console.log(foo);")}, "text/javascript; charset=utf-8"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetMimetype(tt.args.filename, tt.args.data); got != tt.want { + t.Errorf("GetMimetype() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/v2/internal/frontend/assetserver/testdata/index.html b/v2/internal/frontend/assetserver/testdata/index.html new file mode 100644 index 000000000..76da518f4 --- /dev/null +++ b/v2/internal/frontend/assetserver/testdata/index.html @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/v2/internal/frontend/assetserver/testdata/main.css b/v2/internal/frontend/assetserver/testdata/main.css new file mode 100644 index 000000000..57b00e6c6 --- /dev/null +++ b/v2/internal/frontend/assetserver/testdata/main.css @@ -0,0 +1,39 @@ + +html { + text-align: center; + color: white; + background-color: rgba(1, 1, 1, 0.1); +} + +body { + color: white; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + margin: 0; +} + +#result { + margin-top: 1rem; +} + +button { + -webkit-appearance: default-button; + padding: 6px; +} + +#name { + border-radius: 3px; + outline: none; + height: 20px; + -webkit-font-smoothing: antialiased; +} + +#logo { + width: 40%; + height: 40%; + padding-top: 20%; + margin: auto; + display: block; + background-position: center; + background-repeat: no-repeat; + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTUxIDQzNiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMiIgeG1sbnM6dj0iaHR0cHM6Ly92ZWN0YS5pby9uYW5vIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iPjxwYXRoIGQ9Ik0xMDQuMDEgMzQ0LjM4OGgxOC40MDFsLTEuNzY4IDM5LjE2MSAxMi4xNDctMzkuMTYxaDE0Ljg2N2wtLjE4MSAzOS4xNjEgMTAuNTYxLTM5LjE2MWgxOC40MDFsLTIzLjI5NyA2Ni4xNzVoLTE2Ljk5N2wuMTgxLTQxLjM4My0xMi45MTcgNDEuMzgzaC0xNi45NTFsLTIuNDQ3LTY2LjE3NXptMTIwLjk3NSA0My4wNTloNy4zODhsLjIyNy0yNC41MjEtNy42MTUgMjQuNTIxem0tMjUuNzQ0IDIzLjExNmwyNC44ODMtNjYuMTc1aDIxLjgwMWw0LjY2NyA2Ni4xNzVoLTE4LjY3NGwuMDkyLTkuNzQ2aC0xMC45MjRsLTIuOTAxIDkuNzQ2aC0xOC45NDR6bTg4LjE4MyAwbDEwLjQ3LTY2LjE3NmgxOC40OTNsLTEwLjUxNiA2Ni4xNzUtMTguNDQ2LjAwMXptNjUuNzkzIDBsMTAuNTE2LTY2LjE3NWgxOC41MzZsLTcuODg2IDQ5Ljc2NmgxMy41NTJsLTIuNTgyIDE2LjQwOWgtMzIuMTM2em03NC43MjItMjAuMzUyYzIuMDU0IDEuNzIzIDQuMjE1IDMuMDUzIDYuNDgyIDMuOTlzNC40NCAxLjQwNCA2LjUyNiAxLjQwNGMxLjg0MyAwIDMuMzA4LS41MDYgNC4zOTYtMS41MThzMS42MzItMi4zOTUgMS42MzItNC4xNDhjMC0xLjUwOS0uNDU0LTMuMDEzLTEuMzU5LTQuNTA5cy0yLjY2LTMuNDgxLTUuMjU4LTUuOTU5Yy0zLjE0NC0zLjA1Mi01LjMwMy01Ljc0MS02LjQ4Mi04LjA2OXMtMS43NjYtNC44OTQtMS43NjYtNy43MDRjMC02LjMxNSAyLjAwMS0xMS4zMzIgNi4wMDUtMTUuMDQ4czkuNDM0LTUuNTc1IDE2LjI5NC01LjU3NWMyLjc4IDAgNS40MjIuMzEgNy45MzEuOTNzNS4wNiAxLjU3OSA3LjY2MSAyLjg3OGwtMi42MyAxNi4xMzZjLTEuOTk1LTEuMzktMy45MzUtMi40NDctNS44MjMtMy4xNzNzLTMuNjk0LTEuMDg5LTUuNDE3LTEuMDg5Yy0xLjU0MSAwLTIuNzU4LjQtMy42NDkgMS4ycy0xLjMzOCAxLjg5OC0xLjMzOCAzLjI4OGMwIDEuODc1IDEuNzA4IDQuNTAzIDUuMTIzIDcuODg2bC45OTcuOTk2YzMuNDQ1IDMuMzg2IDUuNzExIDYuMjg2IDYuNzk4IDguNzA1czEuNjMxIDUuMjA5IDEuNjMxIDguMzg0YzAgNy4wNzEtMi4xODMgMTIuNjQ2LTYuNTUgMTYuNzI0cy0xMC4zNDEgNi4xMi0xNy45MjUgNi4xMmMtMy4yMzQgMC02LjI5Mi0uMzg1LTkuMTc4LTEuMTU1cy01LjMxLTEuODM4LTcuMjc0LTMuMTk3bDMuMTczLTE3LjQ5NXoiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNLjg4My0uMDgxTC4xMjEuMDgxLjI1Ni0uMDYzLjg4My0uMDgxeiIgZmlsbD0idXJsKCNBKSIgdHJhbnNmb3JtPSJtYXRyaXgoLTE2Ni41OTkgNC41NzEzMiA0LjU3MTMyIDE2Ni41OTkgMTQ3LjQwMyAxNjcuNjQ4KSIvPjxwYXRoIGQ9Ik0uODc4LS4yODVMLS4wNzMuNzEtMS4xODYuNTQyLjAxNS4yMDctLjg0Ni4wNzcuMzU1LS4yNThsLS44Ni0uMTNMLjY0OS0uNzFsLjIyOS40MjV6IiBmaWxsPSJ1cmwoI0IpIiB0cmFuc2Zvcm09Im1hdHJpeCgtMTA2LjQ0MyAtMTYuMDY2OSAtMTYuMDY2OSAxMDYuNDQzIDQyOC4xOSAxODguMDMzKSIvPjxwYXRoIGQ9Ik0uNDQtLjA0aDAgMEwuMjY1LS4wNTYuMTc3LjQzNy0uMzExLS4yNTUuMjYyLS40MzdoLjMwNkwuNDQtLjA0eiIgZmlsbD0idXJsKCNDKSIgdHJhbnNmb3JtPSJtYXRyaXgoLTExNC40ODQgLTE2Mi40MDggLTE2Mi40MDggMTE0LjQ4NCAzMzMuMjkxIDI4NS44MDQpIi8+PHBhdGggZD0iTS41IDBoMCAwIDB6IiBmaWxsPSJ1cmwoI0QpIiB0cmFuc2Zvcm09Im1hdHJpeCg2MS42OTE5IDU4LjgwOTEgNTguODA5MSAtNjEuNjkxOSAyNTguNjMxIDE4MC40MTMpIi8+PHBhdGggZD0iTS42MjItLjExNWguMTM5bC4wNDUuMTAyLjAyLjE5NS0uMjA0LS4yOTd6IiBmaWxsPSJ1cmwoI0UpIiB0cmFuc2Zvcm09Im1hdHJpeCgyMzguMTI2IDI5OC44OTMgMjk4Ljg5MyAtMjM4LjEyNiAxMTMuNTE2IC0xNTAuNTM2KSIvPjxwYXRoIGQ9Ik0uNDY3LjAwNUwuNDkuMDYyLjI3MS0uMDYyLjQ2Ny4wMDV6IiBmaWxsPSJ1cmwoI0YpIiB0cmFuc2Zvcm09Im1hdHJpeCgtMzY5LjUyOSAtOTcuNDExOCAtOTcuNDExOCAzNjkuNTI5IDU4Mi4zOCA5NC4wMjcpIi8+PGcgZmlsbD0idXJsKCNCKSI+PHBhdGggZD0iTS4yLjAwMWwuMDE5LS4wMTkuMzk1LjAzLS4wOTUuMDc3TC4yODIuMDY4LjIuMTM1LjQ2My4xOTQuMzc0LjI2Ni4xMzguMTg2aDAgMEwuMDQ3LjAzMy0uMTMxLS4yNjYuMi4wMDF6IiB0cmFuc2Zvcm09Im1hdHJpeCgtNDk2LjE1NiAtNTMuOTc1MSAtNTMuOTc1MSA0OTYuMTU2IDM2Ny44ODggMTI1LjA4NSkiLz48cGF0aCBkPSJNLjczNSAwaDAgMCAweiIgdHJhbnNmb3JtPSJtYXRyaXgoMTg1LjA3NiAxNzYuNDI3IDE3Ni40MjcgLTE4NS4wNzYgMTUzLjQ0NiA4MC4xNDg4KSIvPjwvZz48L2c+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJBIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLC0zLjQ2OTQ1ZS0xOCwtMy40Njk0NWUtMTgsLTEsMCwtMy4wNTc2MWUtMDYpIiB4bGluazpocmVmPSIjRyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZTMzMjMyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNmIwMDBkIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkIiIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIwIiB4bGluazpocmVmPSIjRyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZTMzMjMyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNmIwMDBkIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkMiIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIwIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsLTEuMTEwMjJlLTE2LC0xLjExMDIyZS0xNiwtMSwwLC0yLjYxODYxZS0wNikiIHhsaW5rOmhyZWY9IiNHIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlMzMyMzIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM2YjAwMGQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iRCIgeDE9IjAiIHkxPSIwIiB4Mj0iMSIgeTI9IjAiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSwtNS41NTExMmUtMTcsLTUuNTUxMTJlLTE3LC0xLDAsLTEuNTc1NjJlLTA2KSIgeGxpbms6aHJlZj0iI0ciPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2UzMzIzMiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzZiMDAwZCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJFIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MDE4OTksLTAuNTk3NDYsLTAuNTk3NDYsMC44MDE4OTksMS4zNDk1LDAuNDQ3NDU3KSIgeGxpbms6aHJlZj0iI0ciPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2UzMzIzMiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzZiMDAwZCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJGIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLC0yLjc3NTU2ZS0xNywtMi43NzU1NmUtMTcsLTEsMCwtMS45MjgyNmUtMDYpIiB4bGluazpocmVmPSIjRyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZTMzMjMyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNmIwMDBkIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIi8+PC9kZWZzPjwvc3ZnPg=="); +} diff --git a/v2/internal/frontend/assetserver/testdata/main.js b/v2/internal/frontend/assetserver/testdata/main.js new file mode 100644 index 000000000..274b4667c --- /dev/null +++ b/v2/internal/frontend/assetserver/testdata/main.js @@ -0,0 +1,20 @@ +import {ready} from '@wails/runtime'; + +ready(() => { + // Get input + focus + let nameElement = document.getElementById("name"); + nameElement.focus(); + + // Setup the greet function + window.greet = function () { + + // Get name + let name = nameElement.value; + + // Call App.Greet(name) + window.backend.main.App.Greet(name).then((result) => { + // Update result with data back from App.Greet() + document.getElementById("result").innerText = result; + }); + }; +}); \ No newline at end of file diff --git a/v2/internal/frontend/assetserver/testdata/subdir/index.html b/v2/internal/frontend/assetserver/testdata/subdir/index.html new file mode 100644 index 000000000..76da518f4 --- /dev/null +++ b/v2/internal/frontend/assetserver/testdata/subdir/index.html @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/v2/internal/frontend/assetserver/testdata/subdir/main.css b/v2/internal/frontend/assetserver/testdata/subdir/main.css new file mode 100644 index 000000000..57b00e6c6 --- /dev/null +++ b/v2/internal/frontend/assetserver/testdata/subdir/main.css @@ -0,0 +1,39 @@ + +html { + text-align: center; + color: white; + background-color: rgba(1, 1, 1, 0.1); +} + +body { + color: white; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + margin: 0; +} + +#result { + margin-top: 1rem; +} + +button { + -webkit-appearance: default-button; + padding: 6px; +} + +#name { + border-radius: 3px; + outline: none; + height: 20px; + -webkit-font-smoothing: antialiased; +} + +#logo { + width: 40%; + height: 40%; + padding-top: 20%; + margin: auto; + display: block; + background-position: center; + background-repeat: no-repeat; + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTUxIDQzNiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMiIgeG1sbnM6dj0iaHR0cHM6Ly92ZWN0YS5pby9uYW5vIj48ZyBmaWxsLXJ1bGU9Im5vbnplcm8iPjxwYXRoIGQ9Ik0xMDQuMDEgMzQ0LjM4OGgxOC40MDFsLTEuNzY4IDM5LjE2MSAxMi4xNDctMzkuMTYxaDE0Ljg2N2wtLjE4MSAzOS4xNjEgMTAuNTYxLTM5LjE2MWgxOC40MDFsLTIzLjI5NyA2Ni4xNzVoLTE2Ljk5N2wuMTgxLTQxLjM4My0xMi45MTcgNDEuMzgzaC0xNi45NTFsLTIuNDQ3LTY2LjE3NXptMTIwLjk3NSA0My4wNTloNy4zODhsLjIyNy0yNC41MjEtNy42MTUgMjQuNTIxem0tMjUuNzQ0IDIzLjExNmwyNC44ODMtNjYuMTc1aDIxLjgwMWw0LjY2NyA2Ni4xNzVoLTE4LjY3NGwuMDkyLTkuNzQ2aC0xMC45MjRsLTIuOTAxIDkuNzQ2aC0xOC45NDR6bTg4LjE4MyAwbDEwLjQ3LTY2LjE3NmgxOC40OTNsLTEwLjUxNiA2Ni4xNzUtMTguNDQ2LjAwMXptNjUuNzkzIDBsMTAuNTE2LTY2LjE3NWgxOC41MzZsLTcuODg2IDQ5Ljc2NmgxMy41NTJsLTIuNTgyIDE2LjQwOWgtMzIuMTM2em03NC43MjItMjAuMzUyYzIuMDU0IDEuNzIzIDQuMjE1IDMuMDUzIDYuNDgyIDMuOTlzNC40NCAxLjQwNCA2LjUyNiAxLjQwNGMxLjg0MyAwIDMuMzA4LS41MDYgNC4zOTYtMS41MThzMS42MzItMi4zOTUgMS42MzItNC4xNDhjMC0xLjUwOS0uNDU0LTMuMDEzLTEuMzU5LTQuNTA5cy0yLjY2LTMuNDgxLTUuMjU4LTUuOTU5Yy0zLjE0NC0zLjA1Mi01LjMwMy01Ljc0MS02LjQ4Mi04LjA2OXMtMS43NjYtNC44OTQtMS43NjYtNy43MDRjMC02LjMxNSAyLjAwMS0xMS4zMzIgNi4wMDUtMTUuMDQ4czkuNDM0LTUuNTc1IDE2LjI5NC01LjU3NWMyLjc4IDAgNS40MjIuMzEgNy45MzEuOTNzNS4wNiAxLjU3OSA3LjY2MSAyLjg3OGwtMi42MyAxNi4xMzZjLTEuOTk1LTEuMzktMy45MzUtMi40NDctNS44MjMtMy4xNzNzLTMuNjk0LTEuMDg5LTUuNDE3LTEuMDg5Yy0xLjU0MSAwLTIuNzU4LjQtMy42NDkgMS4ycy0xLjMzOCAxLjg5OC0xLjMzOCAzLjI4OGMwIDEuODc1IDEuNzA4IDQuNTAzIDUuMTIzIDcuODg2bC45OTcuOTk2YzMuNDQ1IDMuMzg2IDUuNzExIDYuMjg2IDYuNzk4IDguNzA1czEuNjMxIDUuMjA5IDEuNjMxIDguMzg0YzAgNy4wNzEtMi4xODMgMTIuNjQ2LTYuNTUgMTYuNzI0cy0xMC4zNDEgNi4xMi0xNy45MjUgNi4xMmMtMy4yMzQgMC02LjI5Mi0uMzg1LTkuMTc4LTEuMTU1cy01LjMxLTEuODM4LTcuMjc0LTMuMTk3bDMuMTczLTE3LjQ5NXoiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNLjg4My0uMDgxTC4xMjEuMDgxLjI1Ni0uMDYzLjg4My0uMDgxeiIgZmlsbD0idXJsKCNBKSIgdHJhbnNmb3JtPSJtYXRyaXgoLTE2Ni41OTkgNC41NzEzMiA0LjU3MTMyIDE2Ni41OTkgMTQ3LjQwMyAxNjcuNjQ4KSIvPjxwYXRoIGQ9Ik0uODc4LS4yODVMLS4wNzMuNzEtMS4xODYuNTQyLjAxNS4yMDctLjg0Ni4wNzcuMzU1LS4yNThsLS44Ni0uMTNMLjY0OS0uNzFsLjIyOS40MjV6IiBmaWxsPSJ1cmwoI0IpIiB0cmFuc2Zvcm09Im1hdHJpeCgtMTA2LjQ0MyAtMTYuMDY2OSAtMTYuMDY2OSAxMDYuNDQzIDQyOC4xOSAxODguMDMzKSIvPjxwYXRoIGQ9Ik0uNDQtLjA0aDAgMEwuMjY1LS4wNTYuMTc3LjQzNy0uMzExLS4yNTUuMjYyLS40MzdoLjMwNkwuNDQtLjA0eiIgZmlsbD0idXJsKCNDKSIgdHJhbnNmb3JtPSJtYXRyaXgoLTExNC40ODQgLTE2Mi40MDggLTE2Mi40MDggMTE0LjQ4NCAzMzMuMjkxIDI4NS44MDQpIi8+PHBhdGggZD0iTS41IDBoMCAwIDB6IiBmaWxsPSJ1cmwoI0QpIiB0cmFuc2Zvcm09Im1hdHJpeCg2MS42OTE5IDU4LjgwOTEgNTguODA5MSAtNjEuNjkxOSAyNTguNjMxIDE4MC40MTMpIi8+PHBhdGggZD0iTS42MjItLjExNWguMTM5bC4wNDUuMTAyLjAyLjE5NS0uMjA0LS4yOTd6IiBmaWxsPSJ1cmwoI0UpIiB0cmFuc2Zvcm09Im1hdHJpeCgyMzguMTI2IDI5OC44OTMgMjk4Ljg5MyAtMjM4LjEyNiAxMTMuNTE2IC0xNTAuNTM2KSIvPjxwYXRoIGQ9Ik0uNDY3LjAwNUwuNDkuMDYyLjI3MS0uMDYyLjQ2Ny4wMDV6IiBmaWxsPSJ1cmwoI0YpIiB0cmFuc2Zvcm09Im1hdHJpeCgtMzY5LjUyOSAtOTcuNDExOCAtOTcuNDExOCAzNjkuNTI5IDU4Mi4zOCA5NC4wMjcpIi8+PGcgZmlsbD0idXJsKCNCKSI+PHBhdGggZD0iTS4yLjAwMWwuMDE5LS4wMTkuMzk1LjAzLS4wOTUuMDc3TC4yODIuMDY4LjIuMTM1LjQ2My4xOTQuMzc0LjI2Ni4xMzguMTg2aDAgMEwuMDQ3LjAzMy0uMTMxLS4yNjYuMi4wMDF6IiB0cmFuc2Zvcm09Im1hdHJpeCgtNDk2LjE1NiAtNTMuOTc1MSAtNTMuOTc1MSA0OTYuMTU2IDM2Ny44ODggMTI1LjA4NSkiLz48cGF0aCBkPSJNLjczNSAwaDAgMCAweiIgdHJhbnNmb3JtPSJtYXRyaXgoMTg1LjA3NiAxNzYuNDI3IDE3Ni40MjcgLTE4NS4wNzYgMTUzLjQ0NiA4MC4xNDg4KSIvPjwvZz48L2c+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJBIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLC0zLjQ2OTQ1ZS0xOCwtMy40Njk0NWUtMTgsLTEsMCwtMy4wNTc2MWUtMDYpIiB4bGluazpocmVmPSIjRyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZTMzMjMyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNmIwMDBkIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkIiIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIwIiB4bGluazpocmVmPSIjRyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZTMzMjMyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNmIwMDBkIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkMiIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIwIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEsLTEuMTEwMjJlLTE2LC0xLjExMDIyZS0xNiwtMSwwLC0yLjYxODYxZS0wNikiIHhsaW5rOmhyZWY9IiNHIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlMzMyMzIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM2YjAwMGQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iRCIgeDE9IjAiIHkxPSIwIiB4Mj0iMSIgeTI9IjAiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSwtNS41NTExMmUtMTcsLTUuNTUxMTJlLTE3LC0xLDAsLTEuNTc1NjJlLTA2KSIgeGxpbms6aHJlZj0iI0ciPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2UzMzIzMiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzZiMDAwZCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJFIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC44MDE4OTksLTAuNTk3NDYsLTAuNTk3NDYsMC44MDE4OTksMS4zNDk1LDAuNDQ3NDU3KSIgeGxpbms6aHJlZj0iI0ciPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2UzMzIzMiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzZiMDAwZCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJGIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxLC0yLjc3NTU2ZS0xNywtMi43NzU1NmUtMTcsLTEsMCwtMS45MjgyNmUtMDYpIiB4bGluazpocmVmPSIjRyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZTMzMjMyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNmIwMDBkIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9IkciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIi8+PC9kZWZzPjwvc3ZnPg=="); +} diff --git a/v2/internal/frontend/assetserver/testdata/subdir/main.js b/v2/internal/frontend/assetserver/testdata/subdir/main.js new file mode 100644 index 000000000..274b4667c --- /dev/null +++ b/v2/internal/frontend/assetserver/testdata/subdir/main.js @@ -0,0 +1,20 @@ +import {ready} from '@wails/runtime'; + +ready(() => { + // Get input + focus + let nameElement = document.getElementById("name"); + nameElement.focus(); + + // Setup the greet function + window.greet = function () { + + // Get name + let name = nameElement.value; + + // Call App.Greet(name) + window.backend.main.App.Greet(name).then((result) => { + // Update result with data back from App.Greet() + document.getElementById("result").innerText = result; + }); + }; +}); \ No newline at end of file diff --git a/v2/internal/frontend/assetserver/testdata/testdata.go b/v2/internal/frontend/assetserver/testdata/testdata.go new file mode 100644 index 000000000..5387070ec --- /dev/null +++ b/v2/internal/frontend/assetserver/testdata/testdata.go @@ -0,0 +1,6 @@ +package testdata + +import "embed" + +//go:embed index.html main.css main.js +var TopLevelFS embed.FS diff --git a/v2/internal/frontend/calls.go b/v2/internal/frontend/calls.go new file mode 100644 index 000000000..3983c24bf --- /dev/null +++ b/v2/internal/frontend/calls.go @@ -0,0 +1,5 @@ +package frontend + +type Calls interface { + Callback(string) +} diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.h b/v2/internal/frontend/desktop/darwin/AppDelegate.h new file mode 100644 index 000000000..3799aae04 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.h @@ -0,0 +1,20 @@ +// +// AppDelegate.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef AppDelegate_h +#define AppDelegate_h + +#import + +@interface AppDelegate : NSResponder + +@property bool alwaysOnTop; +@property (retain) NSWindow* mainWindow; + +@end + +#endif /* AppDelegate_h */ diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.m b/v2/internal/frontend/desktop/darwin/AppDelegate.m new file mode 100644 index 000000000..24cfaa017 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.m @@ -0,0 +1,53 @@ +// +// AppDelegate.m +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#import +#import + +#import "AppDelegate.h" + +@implementation AppDelegate +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { + return NO; +} +- (void)applicationWillFinishLaunching:(NSNotification *)aNotification { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [self.mainWindow makeKeyAndOrderFront:self]; + if (self.alwaysOnTop) { + [self.mainWindow setLevel:NSStatusWindowLevel]; + } +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + [NSApp activateIgnoringOtherApps:YES]; +} +// +//- (void) CreateMenu { +// [NSApplication sharedApplication]; +// menubar = [[NSMenu new] autorelease]; +// id appMenuItem = [[NSMenuItem new] autorelease]; +// [menubar addItem:appMenuItem]; +// [NSApp setMainMenu:menubar]; +// id appMenu = [[NSMenu new] autorelease]; +// id appName = [[NSProcessInfo processInfo] processName]; +// id quitTitle = [@"Quit " stringByAppendingString:appName]; +// id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle +// action:@selector(terminate:) keyEquivalent:@"q"] +// autorelease]; +// [appMenu addItem:quitMenuItem]; +// [appMenuItem setSubmenu:appMenu]; +//} +// +//- (void) dealloc { +// [super dealloc]; +// window = nil; +// menubar = nil; +//} + +@synthesize touchBar; + +@end diff --git a/v2/internal/frontend/desktop/darwin/Application.h b/v2/internal/frontend/desktop/darwin/Application.h new file mode 100644 index 000000000..05ead41a6 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/Application.h @@ -0,0 +1,44 @@ +// +// Application.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef Application_h +#define Application_h + +#import +#import +#import "WailsContext.h" + +WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug); +void Run(void*); + +void SetTitle(void* ctx, const char *title); +void Center(void* ctx); +void SetSize(void* ctx, int width, int height); +void SetMinSize(void* ctx, int width, int height); +void SetMaxSize(void* ctx, int width, int height); +void SetPosition(void* ctx, int x, int y); +void Fullscreen(void* ctx); +void UnFullscreen(void* ctx); +void Minimise(void* ctx); +void UnMinimise(void* ctx); +void Maximise(void* ctx); +void UnMaximise(void* ctx); +void Hide(void* ctx); +void Show(void* ctx); +void SetRGBA(void* ctx, int r, int g, int b, int a); +void ExecJS(void* ctx, const char*); +void Quit(void*); + +const char* GetSize(void *ctx); +const char* GetPos(void *ctx); + +void ProcessURLResponse(void *inctx, const char *url, const char *contentType, const char *data, int datalength); + +void MessageDialog(void *inctx, const char* dialogType, const char* title, const char* message, const char* button1, const char* button2, const char* button3, const char* button4, const char* defaultButton, const char* cancelButton); +void OpenFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int allowDirectories, int allowFiles, int canCreateDirectories, int treatPackagesAsDirectories, int resolveAliases, int showHiddenFiles, int allowMultipleSelection, const char* filters); +void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters); +#endif /* Application_h */ diff --git a/v2/internal/frontend/desktop/darwin/Application.m b/v2/internal/frontend/desktop/darwin/Application.m new file mode 100644 index 000000000..ac730d523 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/Application.m @@ -0,0 +1,214 @@ +// +// Application.m +// +// Created by Lea Anthony on 10/10/21. +// + +#import +#import +#import "WailsContext.h" +#import "Application.h" +#import "AppDelegate.h" + +WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int debug) { + + WailsContext *result = [WailsContext new]; + + result.debug = debug; + + [result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :appearance :windowIsTranslucent]; + [result SetTitle:title]; + [result Center]; + + result.alwaysOnTop = alwaysOnTop; + result.hideOnClose = hideWindowOnClose; + + return result; +} + +void ProcessURLResponse(void *inctx, const char *url, const char *contentType, const char* data, int datalength) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + NSString *nsurl = [[NSString alloc] initWithUTF8String:url]; + NSString *nsContentType = [[NSString alloc] initWithUTF8String:contentType]; + NSData *nsdata = [NSData dataWithBytes:data length:datalength]; + + [ctx processURLResponse:nsurl :nsContentType :nsdata]; +} + +void ExecJS(void* inctx, const char *script) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx ExecJS:script]; + ); +} + +void SetTitle(void* inctx, const char *title) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx SetTitle:title]; + ); +} + + +void SetRGBA(void *inctx, int r, int g, int b, int a) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx SetRGBA:r :g :b :a]; + ); +} + +void SetSize(void* inctx, int width, int height) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx SetSize:width :height]; + ); +} + +void SetMinSize(void* inctx, int width, int height) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx SetMinSize:width :height]; + ); +} + +void SetMaxSize(void* inctx, int width, int height) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx SetMaxSize:width :height]; + ); +} + +void SetPosition(void* inctx, int x, int y) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx SetSize:x :y]; + ); +} + +void Center(void* inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx Center]; + ); +} + +void Fullscreen(void* inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx Fullscreen]; + ); +} + +void UnFullscreen(void* inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx UnFullscreen]; + ); +} + +void Minimise(void* inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx Minimise]; + ); +} + +void UnMinimise(void* inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx UnMinimise]; + ); +} + +void Maximise(void* inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx Maximise]; + ); +} + +const char* GetSize(void *inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + NSRect frame = [ctx.mainWindow frame]; + NSString *result = [NSString stringWithFormat:@"%d,%d", (int)frame.size.width, (int)frame.size.height]; + return [result UTF8String]; +} + +const char* GetPos(void *inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + NSScreen* screen = [ctx getCurrentScreen]; + NSRect windowFrame = [ctx.mainWindow frame]; + NSRect screenFrame = [screen visibleFrame]; + int x = windowFrame.origin.x - screenFrame.origin.x; + int y = windowFrame.origin.y - screenFrame.origin.y; + y = screenFrame.size.height - y - windowFrame.size.height; + NSString *result = [NSString stringWithFormat:@"%d,%d",x,y]; + return [result UTF8String]; + +} + +void UnMaximise(void* inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx UnMaximise]; + ); +} + +void Quit(void *inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + [NSApp stop:ctx]; +} + +void Hide(void *inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx Hide]; + ); +} + +void Show(void *inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx Show]; + ); +} + +void MessageDialog(void *inctx, const char* dialogType, const char* title, const char* message, const char* button1, const char* button2, const char* button3, const char* button4, const char* defaultButton, const char* cancelButton) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx MessageDialog:dialogType :title :message :button1 :button2 :button3 :button4 :defaultButton :cancelButton]; + ) +} + +void OpenFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int allowDirectories, int allowFiles, int canCreateDirectories, int treatPackagesAsDirectories, int resolveAliases, int showHiddenFiles, int allowMultipleSelection, const char* filters) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx OpenFileDialog:title :defaultFilename :defaultDirectory :allowDirectories :allowFiles :canCreateDirectories :treatPackagesAsDirectories :resolveAliases :showHiddenFiles :allowMultipleSelection :filters]; + ) +} + +void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + ON_MAIN_THREAD( + [ctx SaveFileDialog:title :defaultFilename :defaultDirectory :canCreateDirectories :treatPackagesAsDirectories :showHiddenFiles :filters]; + ) +} + + + + +void Run(void *inctx) { + WailsContext *ctx = (__bridge WailsContext*) inctx; + [NSApplication sharedApplication]; + AppDelegate* delegate = [AppDelegate new]; + [NSApp setDelegate:(id)delegate]; + ctx.appdelegate = delegate; + delegate.mainWindow = ctx.mainWindow; + delegate.alwaysOnTop = ctx.alwaysOnTop; + + [ctx loadRequest:@"wails://wails/"]; + + [NSApp run]; + [ctx release]; + NSLog(@"Here"); +} diff --git a/v2/internal/frontend/desktop/darwin/WailsAlert.h b/v2/internal/frontend/desktop/darwin/WailsAlert.h new file mode 100644 index 000000000..69e8ee170 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsAlert.h @@ -0,0 +1,18 @@ +// +// WailsAlert.h +// test +// +// Created by Lea Anthony on 20/10/21. +// + +#ifndef WailsAlert_h +#define WailsAlert_h + +#import + +@interface WailsAlert : NSAlert +- (void)addButton:(const char*)text :(const char*)defaultButton :(const char*)cancelButton; +@end + + +#endif /* WailsAlert_h */ diff --git a/v2/internal/frontend/desktop/darwin/WailsAlert.m b/v2/internal/frontend/desktop/darwin/WailsAlert.m new file mode 100644 index 000000000..04105634c --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsAlert.m @@ -0,0 +1,30 @@ +// +// WailsAlert.m +// test +// +// Created by Lea Anthony on 20/10/21. +// + +#import + +#import "WailsAlert.h" + +@implementation WailsAlert + +- (void)addButton:(const char*)text :(const char*)defaultButton :(const char*)cancelButton { + if( text == nil ) { + return; + } + NSButton *button = [self addButtonWithTitle:[NSString stringWithUTF8String:text]]; + if( defaultButton != nil && strcmp(text, defaultButton) == 0) { + [button setKeyEquivalent:@"\r"]; + } else if( cancelButton != nil && strcmp(text, cancelButton) == 0) { + [button setKeyEquivalent:@"\033"]; + } else { + [button setKeyEquivalent:@""]; + } +} + +@end + + diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.h b/v2/internal/frontend/desktop/darwin/WailsContext.h new file mode 100644 index 000000000..c4b6e409b --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsContext.h @@ -0,0 +1,74 @@ +// +// WailsContext.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef WailsContext_h +#define WailsContext_h + +#import +#import + +#define ON_MAIN_THREAD(str) dispatch_async(dispatch_get_main_queue(), ^{ str; }); + +@interface WailsWindow : NSWindow +- (BOOL)canBecomeKeyWindow; +@end + +@interface WailsContext : NSObject + +@property (retain) WailsWindow* mainWindow; +@property (retain) WKWebView* webview; +@property (nonatomic, assign) id appdelegate; + +@property bool hideOnClose; +@property bool shuttingDown; + +@property NSSize maxSize; +@property NSSize minSize; + +@property (retain) NSEvent* mouseEvent; + +@property bool alwaysOnTop; +@property bool maximised; + +@property bool debug; + +@property (retain) WKUserContentController* userContentController; + +@property (retain) NSMutableDictionary *urlRequests; + +- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(const char *)appearance :(bool)windowIsTranslucent; +- (void) SetSize:(int)width :(int)height; +- (void) SetPosition:(int)x :(int) y; +- (void) SetMinSize:(int)minWidth :(int)minHeight; +- (void) SetMaxSize:(int)maxWidth :(int)maxHeight; +- (void) SetTitle:(const char*)title; +- (void) Center; +- (void) Fullscreen; +- (void) UnFullscreen; +- (void) Minimise; +- (void) UnMinimise; +- (void) Maximise; +- (void) UnMaximise; +- (void) SetRGBA:(int)r :(int)g :(int)b :(int)a; +- (void) HideMouse; +- (void) ShowMouse; +- (void) Hide; +- (void) Show; + +-(void) MessageDialog :(const char*)dialogType :(const char*)title :(const char*)message :(const char*)button1 :(const char*)button2 :(const char*)button3 :(const char*)button4 :(const char*)defaultButton :(const char*)cancelButton; +-(void) OpenFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(const char*)filters; +-(void) SaveFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(const char*)filters; + +- (void) loadRequest:(NSString*)url; +- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData*)data; +- (void) ExecJS:(const char*)script; +- (NSScreen*) getCurrentScreen; + +@end + + +#endif /* WailsContext_h */ diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.m b/v2/internal/frontend/desktop/darwin/WailsContext.m new file mode 100644 index 000000000..967931f8c --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsContext.m @@ -0,0 +1,527 @@ +// +// WailsContext.m +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#import +#import +#import "WailsContext.h" +#import "WailsAlert.h" +#import "WindowDelegate.h" +#import "message.h" + +@implementation WailsWindow + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +@end + +@implementation WailsContext + +- (void) SetSize:(int)width :(int)height { + + if (self.shuttingDown) return; + + NSRect frame = [self.mainWindow frame]; + frame.origin.y += frame.size.height - height; + frame.size.width = width; + frame.size.height = height; + [self.mainWindow setFrame:frame display:TRUE animate:FALSE]; +} + +- (void) SetPosition:(int)x :(int)y { + + if (self.shuttingDown) return; + + NSScreen* screen = [self getCurrentScreen]; + NSRect windowFrame = [self.mainWindow frame]; + NSRect screenFrame = [screen visibleFrame]; + windowFrame.origin.x += screenFrame.origin.x + (float)x; + windowFrame.origin.y += (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y; + + [self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE]; +} + +- (void) SetMinSize:(int)minWidth :(int)minHeight { + + if (self.shuttingDown) return; + + NSSize size = { minWidth, minHeight }; + + self.minSize = size; + + [self.mainWindow setMinSize:size]; + + [self adjustWindowSize]; +} + + +- (void) SetMaxSize:(int)maxWidth :(int)maxHeight { + + if (self.shuttingDown) return; + + NSSize size = { FLT_MAX, FLT_MAX }; + + size.width = maxWidth > 0 ? maxWidth : FLT_MAX; + size.height = maxHeight > 0 ? maxHeight : FLT_MAX; + + self.maxSize = size; + + [self.mainWindow setMinSize:size]; + + [self adjustWindowSize]; +} + + +- (void) adjustWindowSize { + + if (self.shuttingDown) return; + + NSRect currentFrame = [self.mainWindow frame]; + + if ( currentFrame.size.width > self.maxSize.width ) currentFrame.size.width = self.maxSize.width; + if ( currentFrame.size.width < self.minSize.width ) currentFrame.size.width = self.minSize.width; + if ( currentFrame.size.height > self.maxSize.height ) currentFrame.size.height = self.maxSize.height; + if ( currentFrame.size.height < self.minSize.height ) currentFrame.size.height = self.minSize.height; + + [self.mainWindow setFrame:currentFrame display:TRUE animate:FALSE]; + +} + +- (void) dealloc { + [super dealloc]; + [self.appdelegate release]; + [self.mainWindow release]; + [self.mouseEvent release]; + [self.userContentController release]; + [self.urlRequests release]; +} + +- (NSScreen*) getCurrentScreen { + NSScreen* screen = [self.mainWindow screen]; + if( screen == NULL ) { + screen = [NSScreen mainScreen]; + } + return screen; +} + +- (void) SetTitle:(const char *)title { + NSString *_title = [NSString stringWithUTF8String:title]; + [self.mainWindow setTitle:_title]; +} + +- (void) Center { + [self.mainWindow center]; +} + +- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(const char *)appearance :(bool)windowIsTranslucent { + + self.urlRequests = [NSMutableDictionary new]; + + NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + + if (frameless) { + styleMask = NSWindowStyleMaskBorderless; + } else { + if (resizable) { + styleMask |= NSWindowStyleMaskResizable; + } + } + if (fullscreen) { + styleMask |= NSWindowStyleMaskFullScreen; + } + + if( fullSizeContent || frameless || titlebarAppearsTransparent ) { + styleMask |= NSWindowStyleMaskFullSizeContentView; + } + + self.mainWindow = [[[WailsWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height) + styleMask:styleMask backing:NSBackingStoreBuffered defer:NO] + autorelease]; + + if (frameless) { + return; + } + + if (useToolbar) { + NSLog(@"Using Toolbar"); + id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"]; + [toolbar autorelease]; + [toolbar setShowsBaselineSeparator:!hideToolbarSeparator]; + [self.mainWindow setToolbar:toolbar]; + } + + [self.mainWindow setTitleVisibility:hideTitle]; + [self.mainWindow setTitlebarAppearsTransparent:titlebarAppearsTransparent]; + [self.mainWindow canBecomeKeyWindow]; + + id contentView = [self.mainWindow contentView]; + if (windowIsTranslucent) { + NSVisualEffectView *effectView = [NSVisualEffectView alloc]; + NSRect bounds = [contentView bounds]; + [effectView initWithFrame:bounds]; + [effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; + [effectView setState:NSVisualEffectStateActive]; + [contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil]; + } + + if (appearance != nil) { + NSString *name = [NSString stringWithUTF8String:appearance]; + NSAppearance *nsAppearance = [NSAppearance appearanceNamed:name]; + [self.mainWindow setAppearance:nsAppearance]; + } + + // Set up min/max + NSSize maxSize = { FLT_MAX, FLT_MAX }; + self.maxSize = maxSize; + NSSize minSize = { 0, 0 }; + self.minSize = minSize; + [self adjustWindowSize]; + + WindowDelegate *windowDelegate = [WindowDelegate new]; + windowDelegate.hideOnClose = hideWindowOnClose; + [self.mainWindow setDelegate:windowDelegate]; + + // Webview stuff here! + WKWebViewConfiguration *config = [WKWebViewConfiguration new]; + config.suppressesIncrementalRendering = true; + [config setURLSchemeHandler:self forURLScheme:@"wails"]; + + [config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"]; + + WKUserContentController* userContentController = [WKUserContentController new]; + [userContentController addScriptMessageHandler:self name:@"external"]; + config.userContentController = userContentController; + self.userContentController = userContentController; + if (self.debug) { + [config.preferences setValue:@YES forKey:@"developerExtrasEnabled"]; + } else { + // Disable default context menus + WKUserScript *initScript = [WKUserScript new]; + [initScript initWithSource:@"window.wails.flags.disableWailsDefaultContextMenu = true;" + injectionTime:WKUserScriptInjectionTimeAtDocumentEnd + forMainFrameOnly:false]; + [userContentController addUserScript:initScript]; + + } + + self.webview = [WKWebView alloc]; + CGRect init = { 0,0,0,0 }; + [self.webview initWithFrame:init configuration:config]; + [contentView addSubview:self.webview]; + [self.webview setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable]; + CGRect contentViewBounds = [contentView bounds]; + [self.webview setFrame:contentViewBounds]; + + if (webviewIsTransparent) { + [self.webview setValue:[NSNumber numberWithBool:!webviewIsTransparent] forKey:@"drawsBackground"]; + } + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"]; + + // Mouse monitors + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { + id window = [event window]; + if (window == self.mainWindow) { + self.mouseEvent = event; + } + return event; + }]; + + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseUp handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { + id window = [event window]; + if (window == self.mainWindow) { + self.mouseEvent = nil; + [self ShowMouse]; + } + return event; + }]; + +} + +- (void) loadRequest :(NSString*)url { + NSURL *wkUrl = [NSURL URLWithString:url]; + NSURLRequest *wkRequest = [NSURLRequest requestWithURL:wkUrl]; + [self.webview loadRequest:wkRequest]; +} + +- (void) SetRGBA:(int)r :(int)g :(int)b :(int)a { + float red = r/255; + float green = g/255; + float blue = b/255; + float alpha = a/255; + + id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ]; + + [self.mainWindow setBackgroundColor:colour]; +} + +- (void) HideMouse { + [NSCursor hide]; +} + +- (void) ShowMouse { + [NSCursor unhide]; +} + +- (bool) isFullScreen { + long mask = [self.mainWindow styleMask]; + return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; +} + +// Fullscreen sets the main window to be fullscreen +- (void) Fullscreen { + if( ! [self isFullScreen] ) { + [self.mainWindow toggleFullScreen:nil]; + } +} + +// UnFullscreen resets the main window after a fullscreen +- (void) UnFullscreen { + if( [self isFullScreen] ) { + [self.mainWindow toggleFullScreen:nil]; + } +} + +- (void) Minimise { + [self.mainWindow miniaturize:nil]; +} + +- (void) UnMinimise { + [self.mainWindow deminiaturize:nil]; +} + +- (void) Hide { + [self.mainWindow orderOut:nil]; +} + +- (void) Show { + [self.mainWindow makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; +} + +- (void) Maximise { + if (! self.maximised) { + [self.mainWindow zoom:nil]; + } +} + +- (void) UnMaximise { + if (self.maximised) { + [self.mainWindow zoom:nil]; + } +} + +- (void) ExecJS:(const char*)script { + NSString *nsscript = [NSString stringWithUTF8String:script]; + [self.webview evaluateJavaScript:nsscript completionHandler:nil]; +} + +- (void) processURLResponse:(NSString *)url :(NSString *)contentType :(NSData *)data { + id urlSchemeTask = self.urlRequests[url]; + NSURL *nsurl = [NSURL URLWithString:url]; + + NSHTTPURLResponse *response = [NSHTTPURLResponse new]; + NSMutableDictionary *headerFields = [NSMutableDictionary new]; + headerFields[@"content-type"] = contentType; + [response initWithURL:nsurl statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:headerFields]; + [urlSchemeTask didReceiveResponse:response]; + [urlSchemeTask didReceiveData:data]; + [urlSchemeTask didFinish]; + [self.urlRequests removeObjectForKey:url]; +} + +- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id)urlSchemeTask { + // Do something + self.urlRequests[urlSchemeTask.request.URL.absoluteString] = urlSchemeTask; + processURLRequest(self, [urlSchemeTask.request.URL.absoluteString UTF8String]); +} + +- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id)urlSchemeTask { + +} + +- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { + NSString *m = message.body; + + // Check for drag + if ( [m isEqualToString:@"drag"] ) { + if( ! [self isFullScreen] ) { + if( self.mouseEvent != nil ) { + [self HideMouse]; + ON_MAIN_THREAD( + [self.mainWindow performWindowDragWithEvent:self.mouseEvent]; + ); + } + return; + } + } + + const char *_m = [m UTF8String]; + + processMessage(_m); +} + + +/***** Dialogs ******/ +-(void) MessageDialog :(const char*)dialogType :(const char*)title :(const char*)message :(const char*)button1 :(const char*)button2 :(const char*)button3 :(const char*)button4 :(const char*)defaultButton :(const char*)cancelButton { + + WailsAlert *alert = [WailsAlert new]; + + int style = NSAlertStyleInformational; + if (dialogType != nil ) { + if( strcmp(dialogType, "warning") == 0 ) { + style = NSAlertStyleWarning; + } + if( strcmp(dialogType, "error") == 0) { + style = NSAlertStyleCritical; + } + } + [alert setAlertStyle:style]; + if( strlen(title) > 0 ) { + [alert setMessageText:[NSString stringWithUTF8String:title]]; + } + if( strlen(message) > 0 ) { + [alert setInformativeText:[NSString stringWithUTF8String:message]]; + } + + [alert addButton:button1 :defaultButton :cancelButton]; + [alert addButton:button2 :defaultButton :cancelButton]; + [alert addButton:button3 :defaultButton :cancelButton]; + [alert addButton:button4 :defaultButton :cancelButton]; + + long response = [alert runModal]; + int result; + + if( response == NSAlertFirstButtonReturn ) { + result = 0; + } + else if( response == NSAlertSecondButtonReturn ) { + result = 1; + } + else if( response == NSAlertThirdButtonReturn ) { + result = 2; + } else { + result = 3; + } + processMessageDialogResponse(result); +} + +-(void) OpenFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(const char*)filters { + + + // Create the dialog + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + + // Valid but appears to do nothing.... :/ + if( strlen(title) > 0 ) { + [dialog setTitle:[NSString stringWithUTF8String:title]]; + } + + // Filters - semicolon delimited list of file extensions + if( allowFiles ) { + if( filters != nil && strlen(filters) > 0) { + NSString *filterString = [[NSString stringWithUTF8String:filters] stringByReplacingOccurrencesOfString:@"*." withString:@""]; + filterString = [filterString stringByReplacingOccurrencesOfString:@" " withString:@""]; + NSArray *filterList = [filterString componentsSeparatedByString:@";"]; + [dialog setAllowedFileTypes:filterList]; + } else { + [dialog setAllowsOtherFileTypes:true]; + } + // Default Filename + if( defaultFilename != NULL && strlen(defaultFilename) > 0 ) { + [dialog setNameFieldStringValue:[NSString stringWithUTF8String:defaultFilename]]; + } + + [dialog setAllowsMultipleSelection: allowMultipleSelection]; + [dialog setShowsHiddenFiles: showHiddenFiles]; + + } + + // Default Directory + if( defaultDirectory != NULL && strlen(defaultDirectory) > 0 ) { + NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:defaultDirectory]]; + [dialog setDirectoryURL:url]; + } + + + // Setup Options + [dialog setCanChooseFiles: allowFiles]; + [dialog setCanChooseDirectories: allowDirectories]; + [dialog setCanCreateDirectories: canCreateDirectories]; + [dialog setResolvesAliases: resolveAliases]; + [dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories]; + + // Setup callback handler + [dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) { + NSMutableArray *arr = [NSMutableArray new]; + for (NSURL *url in [dialog URLs]) { + [arr addObject:[url path]]; + } + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:0 error:nil]; + NSString *nsjson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + processOpenFileDialogResponse([nsjson UTF8String]); + }]; + + + [dialog runModal]; + +} + + +-(void) SaveFileDialog :(const char*)title :(const char*)defaultFilename :(const char*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(const char*)filters; { + + + // Create the dialog + NSSavePanel *dialog = [NSOpenPanel savePanel]; + + // Valid but appears to do nothing.... :/ + if( strlen(title) > 0 ) { + [dialog setTitle:[NSString stringWithUTF8String:title]]; + } + + // Filters - semicolon delimited list of file extensions + if( filters != nil && strlen(filters) > 0) { + NSString *filterString = [[NSString stringWithUTF8String:filters] stringByReplacingOccurrencesOfString:@"*." withString:@""]; + filterString = [filterString stringByReplacingOccurrencesOfString:@" " withString:@""]; + NSArray *filterList = [filterString componentsSeparatedByString:@";"]; + [dialog setAllowedFileTypes:filterList]; + } else { + [dialog setAllowsOtherFileTypes:true]; + } + // Default Filename + if( defaultFilename != NULL && strlen(defaultFilename) > 0 ) { + [dialog setNameFieldStringValue:[NSString stringWithUTF8String:defaultFilename]]; + } + + // Default Directory + if( defaultDirectory != NULL && strlen(defaultDirectory) > 0 ) { + NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:defaultDirectory]]; + [dialog setDirectoryURL:url]; + } + + // Setup Options + [dialog setCanCreateDirectories: canCreateDirectories]; + [dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories]; + [dialog setShowsHiddenFiles: showHiddenFiles]; + + // Setup callback handler + [dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) { + NSURL *url = [dialog URL]; + processSaveFileDialogResponse([url.path UTF8String]); + }]; + + [dialog runModal]; + +} + + +@end + diff --git a/v2/internal/frontend/desktop/darwin/WindowDelegate.h b/v2/internal/frontend/desktop/darwin/WindowDelegate.h new file mode 100644 index 000000000..e4ba038d1 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WindowDelegate.h @@ -0,0 +1,18 @@ +// +// WindowDelegate.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef WindowDelegate_h +#define WindowDelegate_h + +@interface WindowDelegate : NSObject + +@property bool hideOnClose; + +@end + + +#endif /* WindowDelegate_h */ diff --git a/v2/internal/frontend/desktop/darwin/WindowDelegate.m b/v2/internal/frontend/desktop/darwin/WindowDelegate.m new file mode 100644 index 000000000..549413e45 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WindowDelegate.m @@ -0,0 +1,23 @@ +// +// WindowDelegate.m +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#import +#import +#import "WindowDelegate.h" +#import "message.h" + +@implementation WindowDelegate + +- (BOOL)windowShouldClose:(NSWindow *)sender { + [sender orderOut:nil]; + if( self.hideOnClose == false ) { + processMessage("Q"); + } + return !self.hideOnClose; +} + +@end diff --git a/v2/internal/frontend/desktop/darwin/browser.go b/v2/internal/frontend/desktop/darwin/browser.go new file mode 100644 index 000000000..417501c8e --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/browser.go @@ -0,0 +1,14 @@ +//go:build darwin +// +build darwin + +package darwin + +import ( + "github.com/pkg/browser" +) + +// BrowserOpenURL Use the default browser to open the url +func (f *Frontend) BrowserOpenURL(url string) { + // Specific method implementation + _ = browser.OpenURL(url) +} diff --git a/v2/internal/frontend/desktop/darwin/calloc.go b/v2/internal/frontend/desktop/darwin/calloc.go new file mode 100644 index 000000000..b1939900e --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/calloc.go @@ -0,0 +1,32 @@ +package darwin + +/* +#include +*/ +import "C" +import "unsafe" + +// Calloc handles alloc/dealloc of C data +type Calloc struct { + pool []unsafe.Pointer +} + +// NewCalloc creates a new allocator +func NewCalloc() Calloc { + return Calloc{} +} + +// String creates a new C string and retains a reference to it +func (c Calloc) String(in string) *C.char { + result := C.CString(in) + c.pool = append(c.pool, unsafe.Pointer(result)) + return result +} + +// Free frees all allocated C memory +func (c Calloc) Free() { + for _, str := range c.pool { + C.free(str) + } + c.pool = []unsafe.Pointer{} +} diff --git a/v2/internal/frontend/desktop/darwin/dialog.go b/v2/internal/frontend/desktop/darwin/dialog.go new file mode 100644 index 000000000..9e359f249 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/dialog.go @@ -0,0 +1,185 @@ +//go:build darwin +// +build darwin + +package darwin + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit +#import +#import "Application.h" +#import "WailsContext.h" +*/ +import "C" +import ( + "encoding/json" + "fmt" + "strings" + "sync" + + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/frontend" +) + +// Obj-C dialog methods send the response to this channel +var messageDialogResponse = make(chan int) +var openFileDialogResponse = make(chan string) +var saveFileDialogResponse = make(chan string) +var dialogLock sync.Mutex + +// OpenDirectoryDialog prompts the user to select a directory +func (f *Frontend) OpenDirectoryDialog(options frontend.OpenDialogOptions) (string, error) { + results, err := f.openDialog(&options, false, false, true) + if err != nil { + return "", err + } + var selected string + if len(results) > 0 { + selected = results[0] + } + return selected, nil +} + +func (f *Frontend) openDialog(options *frontend.OpenDialogOptions, multiple bool, allowfiles bool, allowdirectories bool) ([]string, error) { + dialogLock.Lock() + defer dialogLock.Unlock() + + c := NewCalloc() + defer c.Free() + title := c.String(options.Title) + defaultFilename := c.String(options.DefaultFilename) + defaultDirectory := c.String(options.DefaultDirectory) + allowDirectories := bool2Cint(allowdirectories) + allowFiles := bool2Cint(allowfiles) + canCreateDirectories := bool2Cint(options.CanCreateDirectories) + treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories) + resolveAliases := bool2Cint(options.ResolvesAliases) + showHiddenFiles := bool2Cint(options.ShowHiddenFiles) + allowMultipleFileSelection := bool2Cint(multiple) + + var filterStrings slicer.StringSlicer + if options.Filters != nil { + for _, filter := range options.Filters { + thesePatterns := strings.Split(filter.Pattern, ";") + for _, pattern := range thesePatterns { + pattern = strings.TrimSpace(pattern) + if pattern != "" { + filterStrings.Add(pattern) + } + } + } + filterStrings.Deduplicate() + } + filters := filterStrings.Join(";") + C.OpenFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, allowDirectories, allowFiles, canCreateDirectories, treatPackagesAsDirectories, resolveAliases, showHiddenFiles, allowMultipleFileSelection, c.String(filters)) + + var result = <-openFileDialogResponse + + var parsedResults []string + err := json.Unmarshal([]byte(result), &parsedResults) + + return parsedResults, err +} + +// OpenFileDialog prompts the user to select a file +func (f *Frontend) OpenFileDialog(options frontend.OpenDialogOptions) (string, error) { + results, err := f.openDialog(&options, false, options.AllowFiles, options.AllowDirectories) + if err != nil { + return "", err + } + var selected string + if len(results) > 0 { + selected = results[0] + } + return selected, nil +} + +// OpenMultipleFilesDialog prompts the user to select a file +func (f *Frontend) OpenMultipleFilesDialog(options frontend.OpenDialogOptions) ([]string, error) { + return f.openDialog(&options, true, options.AllowFiles, options.AllowDirectories) +} + +// SaveFileDialog prompts the user to select a file +func (f *Frontend) SaveFileDialog(options frontend.SaveDialogOptions) (string, error) { + dialogLock.Lock() + defer dialogLock.Unlock() + + c := NewCalloc() + defer c.Free() + title := c.String(options.Title) + defaultFilename := c.String(options.DefaultFilename) + defaultDirectory := c.String(options.DefaultDirectory) + canCreateDirectories := bool2Cint(options.CanCreateDirectories) + treatPackagesAsDirectories := bool2Cint(options.TreatPackagesAsDirectories) + showHiddenFiles := bool2Cint(options.ShowHiddenFiles) + + var filterStrings slicer.StringSlicer + if options.Filters != nil { + for _, filter := range options.Filters { + thesePatterns := strings.Split(filter.Pattern, ";") + for _, pattern := range thesePatterns { + pattern = strings.TrimSpace(pattern) + if pattern != "" { + filterStrings.Add(pattern) + } + } + } + filterStrings.Deduplicate() + } + filters := filterStrings.Join(";") + C.SaveFileDialog(f.mainWindow.context, title, defaultFilename, defaultDirectory, canCreateDirectories, treatPackagesAsDirectories, showHiddenFiles, c.String(filters)) + + var result = <-saveFileDialogResponse + + return result, nil +} + +// MessageDialog show a message dialog to the user +func (f *Frontend) MessageDialog(options frontend.MessageDialogOptions) (string, error) { + dialogLock.Lock() + defer dialogLock.Unlock() + + c := NewCalloc() + defer c.Free() + dialogType := c.String(string(options.Type)) + title := c.String(options.Title) + message := c.String(options.Message) + defaultButton := c.String(options.DefaultButton) + cancelButton := c.String(options.CancelButton) + const MaxButtons = 4 + var buttons [MaxButtons]*C.char + for index, buttonText := range options.Buttons { + if index == MaxButtons { + return "", fmt.Errorf("max %d buttons supported (%d given)", MaxButtons, len(options.Buttons)) + } + buttons[index] = c.String(buttonText) + } + + C.MessageDialog(f.mainWindow.context, dialogType, title, message, buttons[0], buttons[1], buttons[2], buttons[3], defaultButton, cancelButton) + + var result = <-messageDialogResponse + + selectedC := buttons[result] + var selected string + if selectedC != nil { + selected = options.Buttons[result] + } + return selected, nil +} + +//export processMessageDialogResponse +func processMessageDialogResponse(selection int) { + messageDialogResponse <- selection +} + +//export processOpenFileDialogResponse +func processOpenFileDialogResponse(cselection *C.char) { + selection := C.GoString(cselection) + openFileDialogResponse <- selection +} + +//export processSaveFileDialogResponse +func processSaveFileDialogResponse(cselection *C.char) { + selection := C.GoString(cselection) + saveFileDialogResponse <- selection +} diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go new file mode 100644 index 000000000..04f366f86 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -0,0 +1,306 @@ +//go:build darwin +// +build darwin + +package darwin + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit +#import +#import "Application.h" +#import "WailsContext.h" + +#include +*/ +import "C" +import ( + "context" + "encoding/json" + "html/template" + "log" + "strconv" + "strings" + "unsafe" + + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/frontend/assetserver" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options" +) + +type request struct { + url *C.char + ctx unsafe.Pointer +} + +var messageBuffer = make(chan string, 100) +var requestBuffer = make(chan *request, 100) + +type Frontend struct { + + // Context + ctx context.Context + + frontendOptions *options.App + logger *logger.Logger + debug bool + + // Assets + assets *assetserver.DesktopAssetServer + + // main window handle + mainWindow *Window + minWidth, minHeight, maxWidth, maxHeight int + bindings *binding.Bindings + dispatcher frontend.Dispatcher + servingFromDisk bool +} + +func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend { + + result := &Frontend{ + frontendOptions: appoptions, + logger: myLogger, + bindings: appBindings, + dispatcher: dispatcher, + ctx: ctx, + minHeight: appoptions.MinHeight, + minWidth: appoptions.MinWidth, + maxHeight: appoptions.MaxHeight, + maxWidth: appoptions.MaxWidth, + } + + // Check if we have been given a directory to serve assets from. + // If so, this means we are in dev mode and are serving assets off disk. + // We indicate this through the `servingFromDisk` flag to ensure requests + // aren't cached by WebView2 in dev mode + _assetdir := ctx.Value("assetdir") + if _assetdir != nil { + result.servingFromDisk = true + } + + bindingsJSON, err := appBindings.ToJSON() + if err != nil { + log.Fatal(err) + } + assets, err := assetserver.NewDesktopAssetServer(ctx, appoptions.Assets, bindingsJSON) + if err != nil { + log.Fatal(err) + } + result.assets = assets + + go result.startMessageProcessor() + go result.startRequestProcessor() + + return result +} + +func (f *Frontend) startMessageProcessor() { + for message := range messageBuffer { + f.processMessage(message) + } +} +func (f *Frontend) startRequestProcessor() { + for request := range requestBuffer { + f.processRequest(request) + } +} + +func (f *Frontend) WindowReload() { + f.ExecJS("runtime.WindowReload();") +} + +func (f *Frontend) Run(ctx context.Context) error { + + f.ctx = context.WithValue(ctx, "frontend", f) + + var _debug = ctx.Value("debug") + if _debug != nil { + f.debug = _debug.(bool) + } + + mainWindow := NewWindow(f.frontendOptions, f.debug) + f.mainWindow = mainWindow + f.mainWindow.Center() + + go func() { + if f.frontendOptions.OnStartup != nil { + f.frontendOptions.OnStartup(f.ctx) + } + }() + mainWindow.Run() + return nil +} + +func (f *Frontend) WindowCenter() { + f.mainWindow.Center() +} + +func (f *Frontend) WindowSetPos(x, y int) { + f.mainWindow.SetPos(x, y) +} +func (f *Frontend) WindowGetPos() (int, int) { + return f.mainWindow.Pos() +} + +func (f *Frontend) WindowSetSize(width, height int) { + f.mainWindow.SetSize(width, height) +} + +func (f *Frontend) WindowGetSize() (int, int) { + return f.mainWindow.Size() +} + +func (f *Frontend) WindowSetTitle(title string) { + f.mainWindow.SetTitle(title) +} + +func (f *Frontend) WindowFullscreen() { + f.mainWindow.SetMaxSize(0, 0) + f.mainWindow.SetMinSize(0, 0) + f.mainWindow.Fullscreen() +} + +func (f *Frontend) WindowUnFullscreen() { + f.mainWindow.UnFullscreen() + f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight) + f.mainWindow.SetMinSize(f.minWidth, f.minHeight) +} + +func (f *Frontend) WindowShow() { + f.mainWindow.Show() +} + +func (f *Frontend) WindowHide() { + f.mainWindow.Hide() +} +func (f *Frontend) WindowMaximise() { + f.mainWindow.Maximise() +} +func (f *Frontend) WindowUnmaximise() { + f.mainWindow.UnMaximise() +} +func (f *Frontend) WindowMinimise() { + f.mainWindow.Minimise() +} +func (f *Frontend) WindowUnminimise() { + f.mainWindow.UnMinimise() +} + +func (f *Frontend) WindowSetMinSize(width int, height int) { + f.minWidth = width + f.minHeight = height + f.mainWindow.SetMinSize(width, height) +} +func (f *Frontend) WindowSetMaxSize(width int, height int) { + f.maxWidth = width + f.maxHeight = height + f.mainWindow.SetMaxSize(width, height) +} + +func (f *Frontend) WindowSetRGBA(col *options.RGBA) { + if col == nil { + return + } + f.mainWindow.SetRGBA(col.R, col.G, col.B, col.A) +} + +func (f *Frontend) Quit() { + f.mainWindow.Quit() +} + +type EventNotify struct { + Name string `json:"name"` + Data []interface{} `json:"data"` +} + +func (f *Frontend) Notify(name string, data ...interface{}) { + notification := EventNotify{ + Name: name, + Data: data, + } + payload, err := json.Marshal(notification) + if err != nil { + f.logger.Error(err.Error()) + return + } + f.ExecJS(`window.wails.EventsNotify('` + template.JSEscapeString(string(payload)) + `');`) +} + +func (f *Frontend) processMessage(message string) { + if message == "drag" { + err := f.startDrag() + if err != nil { + f.logger.Error(err.Error()) + } + return + } + result, err := f.dispatcher.ProcessMessage(message, f) + if err != nil { + f.logger.Error(err.Error()) + f.Callback(result) + return + } + if result == "" { + return + } + + switch result[0] { + case 'c': + // Callback from a method call + f.Callback(result[1:]) + default: + f.logger.Info("Unknown message returned from dispatcher: %+v", result) + } +} + +func (f *Frontend) Callback(message string) { + f.ExecJS(`window.wails.Callback(` + strconv.Quote(message) + `);`) +} + +func (f *Frontend) startDrag() error { + //if !w32.ReleaseCapture() { + // return fmt.Errorf("unable to release mouse capture") + //} + //w32.SendMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0) + return nil +} + +func (f *Frontend) ExecJS(js string) { + f.mainWindow.ExecJS(js) +} + +func (f *Frontend) processRequest(r *request) { + url := C.GoString(r.url) + url = strings.TrimPrefix(url, "wails://wails") + if !strings.HasPrefix(url, "/") { + return + } + _contents, _mimetype, err := f.assets.Load(url) + if err != nil { + f.logger.Error(err.Error()) + //TODO: Handle errors + return + } + data := C.CString(string(_contents)) + defer C.free(unsafe.Pointer(data)) + mimetype := C.CString(_mimetype) + defer C.free(unsafe.Pointer(mimetype)) + + C.ProcessURLResponse(r.ctx, r.url, mimetype, data, C.int(len(_contents))) +} + +//export processMessage +func processMessage(message *C.char) { + goMessage := C.GoString(message) + messageBuffer <- goMessage +} + +//export processURLRequest +func processURLRequest(ctx unsafe.Pointer, url *C.char) { + requestBuffer <- &request{ + url: url, + ctx: ctx, + } +} diff --git a/v2/internal/frontend/desktop/darwin/main.m b/v2/internal/frontend/desktop/darwin/main.m new file mode 100644 index 000000000..d605d8282 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/main.m @@ -0,0 +1,56 @@ +//go:build ignore +// main.m +// test +// +// Created by Lea Anthony on 10/10/21. +// + +// ****** This file is used for testing purposes only ****** + +#import +#import "Application.h" + +void processMessage(const char*t) { + NSLog(@"processMessage called"); + +} + +void processMessageDialogResponse(int t) { + NSLog(@"processMessage called"); +} + +void processOpenFileDialogResponse(const char *t) { + NSLog(@"processMessage called %s", t); +} + +void processURLRequest(void *ctx, const char* url) { + NSLog(@"processURLRequest called"); + const char myByteArray[] = { 0x3c,0x68,0x31,0x3e,0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21,0x3c,0x2f,0x68,0x31,0x3e }; + ProcessURLResponse(ctx, url, "text/html", myByteArray, 21); +} + +int main(int argc, const char * argv[]) { + // insert code here... + int frameless = 0; + int resizable = 1; + int fullscreen = 0; + int fullSizeContent = 1; + int hideTitleBar = 0; + int titlebarAppearsTransparent = 1; + int hideTitle = 0; + int useToolbar = 1; + int hideToolbarSeparator = 1; + int webviewIsTransparent = 0; + int alwaysOnTop = 1; + int hideWindowOnClose = 0; + const char* appearance = "NSAppearanceNameDarkAqua"; + int windowIsTranslucent = 1; + int debug = 1; + WailsContext *result = Create("OI OI!",400,400, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug); + SetRGBA(result, 255, 0, 0, 255); + + + + Run((void*)CFBridgingRetain(result)); + return 0; +} diff --git a/v2/internal/frontend/desktop/darwin/menu.go b/v2/internal/frontend/desktop/darwin/menu.go new file mode 100644 index 000000000..3ccb156cf --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/menu.go @@ -0,0 +1,98 @@ +//go:build darwin +// +build darwin + +package darwin + +import ( + "github.com/wailsapp/wails/v2/pkg/menu" +) + +//func (w *Window) SetApplicationMenu(menu *menu.Menu) { +//w.applicationMenu = menu +//processMenu(w, menu) +//} + +//func processMenu(window *Window, menu *menu.Menu) { +//mainMenu := window.NewMenu() +//for _, menuItem := range menu.Items { +// submenu := mainMenu.AddSubMenu(menuItem.Label) +// for _, menuItem := range menuItem.SubMenu.Items { +// processMenuItem(submenu, menuItem) +// } +//} +//mainMenu.Show() +//} + +//func processMenuItem(parent *winc.MenuItem, menuItem *menu.MenuItem) { +// if menuItem.Hidden { +// return +// } +// switch menuItem.Type { +// case menu.SeparatorType: +// parent.AddSeparator() +// case menu.TextType: +// shortcut := acceleratorToWincShortcut(menuItem.Accelerator) +// newItem := parent.AddItem(menuItem.Label, shortcut) +// if menuItem.Tooltip != "" { +// newItem.SetToolTip(menuItem.Tooltip) +// } +// if menuItem.Click != nil { +// newItem.OnClick().Bind(func(e *winc.Event) { +// menuItem.Click(&menu.CallbackData{ +// MenuItem: menuItem, +// }) +// }) +// } +// newItem.SetEnabled(!menuItem.Disabled) +// +// case menu.CheckboxType: +// shortcut := acceleratorToWincShortcut(menuItem.Accelerator) +// newItem := parent.AddItem(menuItem.Label, shortcut) +// newItem.SetCheckable(true) +// newItem.SetChecked(menuItem.Checked) +// if menuItem.Tooltip != "" { +// newItem.SetToolTip(menuItem.Tooltip) +// } +// if menuItem.Click != nil { +// newItem.OnClick().Bind(func(e *winc.Event) { +// toggleCheckBox(menuItem) +// menuItem.Click(&menu.CallbackData{ +// MenuItem: menuItem, +// }) +// }) +// } +// newItem.SetEnabled(!menuItem.Disabled) +// addCheckBoxToMap(menuItem, newItem) +// case menu.RadioType: +// shortcut := acceleratorToWincShortcut(menuItem.Accelerator) +// newItem := parent.AddItemRadio(menuItem.Label, shortcut) +// newItem.SetCheckable(true) +// newItem.SetChecked(menuItem.Checked) +// if menuItem.Tooltip != "" { +// newItem.SetToolTip(menuItem.Tooltip) +// } +// if menuItem.Click != nil { +// newItem.OnClick().Bind(func(e *winc.Event) { +// toggleRadioItem(menuItem) +// menuItem.Click(&menu.CallbackData{ +// MenuItem: menuItem, +// }) +// }) +// } +// newItem.SetEnabled(!menuItem.Disabled) +// addRadioItemToMap(menuItem, newItem) +// case menu.SubmenuType: +// submenu := parent.AddSubMenu(menuItem.Label) +// for _, menuItem := range menuItem.SubMenu.Items { +// processMenuItem(submenu, menuItem) +// } +// } +//} + +func (f *Frontend) MenuSetApplicationMenu(menu *menu.Menu) { + //f.mainWindow.SetApplicationMenu(menu) +} + +func (f *Frontend) MenuUpdateApplicationMenu() { + //processMenu(f.mainWindow, f.mainWindow.applicationMenu) +} diff --git a/v2/internal/frontend/desktop/darwin/message.h b/v2/internal/frontend/desktop/darwin/message.h new file mode 100644 index 000000000..51f242ccc --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/message.h @@ -0,0 +1,28 @@ +// +// message.h +// test +// +// Created by Lea Anthony on 14/10/21. +// + +#ifndef export_h +#define export_h + + +#ifdef __cplusplus +extern "C" +{ +#endif + +void processMessage(const char *); +void processURLRequest(void*, const char *); +void processMessageDialogResponse(int); +void processOpenFileDialogResponse(const char*); +void processSaveFileDialogResponse(const char*); + +#ifdef __cplusplus +} +#endif + + +#endif /* export_h */ diff --git a/v2/internal/frontend/desktop/darwin/window.go b/v2/internal/frontend/desktop/darwin/window.go new file mode 100644 index 000000000..b12937d42 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/window.go @@ -0,0 +1,187 @@ +package darwin + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit +#import +#import "Application.h" +#import "WailsContext.h" + +#include +*/ +import "C" +import ( + "log" + "runtime" + "strconv" + "strings" + "unsafe" + + "github.com/wailsapp/wails/v2/pkg/options" +) + +func init() { + runtime.LockOSThread() +} + +type Window struct { + context unsafe.Pointer +} + +func bool2Cint(value bool) C.int { + if value { + return C.int(1) + } + return C.int(0) +} + +func NewWindow(frontendOptions *options.App, debugMode bool) *Window { + + c := NewCalloc() + defer c.Free() + + frameless := bool2Cint(frontendOptions.Frameless) + resizable := bool2Cint(!frontendOptions.DisableResize) + fullscreen := bool2Cint(frontendOptions.Fullscreen) + alwaysOnTop := bool2Cint(frontendOptions.AlwaysOnTop) + hideWindowOnClose := bool2Cint(frontendOptions.HideWindowOnClose) + debug := bool2Cint(debugMode) + alpha := C.int(frontendOptions.RGBA.A) + red := C.int(frontendOptions.RGBA.R) + green := C.int(frontendOptions.RGBA.G) + blue := C.int(frontendOptions.RGBA.B) + + var fullSizeContent, hideTitleBar, hideTitle, useToolbar, webviewIsTransparent C.int + var titlebarAppearsTransparent, hideToolbarSeparator, windowIsTranslucent C.int + var appearance, title *C.char + + width := C.int(frontendOptions.Width) + height := C.int(frontendOptions.Height) + + title = c.String(frontendOptions.Title) + + if frontendOptions.Mac != nil { + mac := frontendOptions.Mac + if mac.TitleBar != nil { + fullSizeContent = bool2Cint(mac.TitleBar.FullSizeContent) + hideTitleBar = bool2Cint(mac.TitleBar.HideTitleBar) + hideTitle = bool2Cint(mac.TitleBar.HideTitle) + useToolbar = bool2Cint(mac.TitleBar.UseToolbar) + titlebarAppearsTransparent = bool2Cint(mac.TitleBar.TitlebarAppearsTransparent) + hideToolbarSeparator = bool2Cint(mac.TitleBar.HideToolbarSeparator) + } + windowIsTranslucent = bool2Cint(mac.WindowIsTranslucent) + webviewIsTransparent = bool2Cint(mac.WebviewIsTransparent) + + appearance = c.String(string(mac.Appearance)) + } + var context *C.WailsContext = C.Create(title, width, height, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent, debug) + + C.SetRGBA(unsafe.Pointer(context), red, green, blue, alpha) + + return &Window{ + context: unsafe.Pointer(context), + } +} + +func (w *Window) Center() { + C.Center(w.context) +} + +func (w *Window) Run() { + C.Run(w.context) + println("I exited!") +} + +func (w *Window) Quit() { + C.Quit(w.context) +} + +func (w *Window) SetRGBA(r uint8, g uint8, b uint8, a uint8) { + C.SetRGBA(w.context, C.int(r), C.int(g), C.int(b), C.int(a)) +} + +func (w *Window) ExecJS(js string) { + _js := C.CString(js) + C.ExecJS(w.context, _js) + C.free(unsafe.Pointer(_js)) +} + +func (w *Window) SetPos(x int, y int) { + C.SetPosition(w.context, C.int(x), C.int(y)) +} + +func (w *Window) SetSize(width int, height int) { + C.SetSize(w.context, C.int(width), C.int(height)) +} + +func (w *Window) SetTitle(title string) { + t := C.CString(title) + C.SetTitle(w.context, t) + C.free(unsafe.Pointer(t)) +} + +func (w *Window) Maximise() { + C.Maximise(w.context) +} + +func (w *Window) UnMaximise() { + C.UnMaximise(w.context) +} + +func (w *Window) Minimise() { + C.Minimise(w.context) +} + +func (w *Window) UnMinimise() { + C.UnMinimise(w.context) +} + +func (w *Window) SetMinSize(width int, height int) { + C.SetMinSize(w.context, C.int(width), C.int(height)) +} + +func (w *Window) SetMaxSize(width int, height int) { + C.SetMaxSize(w.context, C.int(width), C.int(height)) +} + +func (w *Window) Fullscreen() { + C.Fullscreen(w.context) +} + +func (w *Window) UnFullscreen() { + C.UnFullscreen(w.context) +} + +func (w *Window) Show() { + C.Show(w.context) +} + +func (w *Window) Hide() { + C.Hide(w.context) +} + +func parseIntDuo(temp string) (int, int) { + split := strings.Split(temp, ",") + x, err := strconv.Atoi(split[0]) + if err != nil { + log.Fatal(err) + } + y, err := strconv.Atoi(split[1]) + if err != nil { + log.Fatal(err) + } + return x, y +} + +func (w *Window) Pos() (int, int) { + var _result *C.char = C.GetPos(w.context) + temp := C.GoString(_result) + return parseIntDuo(temp) +} + +func (w *Window) Size() (int, int) { + var _result *C.char = C.GetSize(w.context) + temp := C.GoString(_result) + return parseIntDuo(temp) +} diff --git a/v2/internal/frontend/desktop/desktop_darwin.go b/v2/internal/frontend/desktop/desktop_darwin.go new file mode 100644 index 000000000..fbf977fd5 --- /dev/null +++ b/v2/internal/frontend/desktop/desktop_darwin.go @@ -0,0 +1,19 @@ +//go:build darwin + +package desktop + +import ( + "context" + + "github.com/wailsapp/wails/v2/internal/frontend/desktop/darwin" + + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options" +) + +func NewFrontend(ctx context.Context, appoptions *options.App, logger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) frontend.Frontend { + return darwin.NewFrontend(ctx, appoptions, logger, appBindings, dispatcher) +} diff --git a/v2/internal/frontend/desktop/desktop_windows.go b/v2/internal/frontend/desktop/desktop_windows.go new file mode 100644 index 000000000..f3c8e05d9 --- /dev/null +++ b/v2/internal/frontend/desktop/desktop_windows.go @@ -0,0 +1,16 @@ +//go:build windows + +package desktop + +import ( + "context" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/frontend/desktop/windows" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options" +) + +func NewFrontend(ctx context.Context, appoptions *options.App, logger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) frontend.Frontend { + return windows.NewFrontend(ctx, appoptions, logger, appBindings, dispatcher) +} diff --git a/v2/internal/frontend/desktop/windows/browser.go b/v2/internal/frontend/desktop/windows/browser.go new file mode 100644 index 000000000..f23b04dbe --- /dev/null +++ b/v2/internal/frontend/desktop/windows/browser.go @@ -0,0 +1,14 @@ +//go:build windows +// +build windows + +package windows + +import ( + "github.com/pkg/browser" +) + +// BrowserOpenURL Use the default browser to open the url +func (f *Frontend) BrowserOpenURL(url string) { + // Specific method implementation + _ = browser.OpenURL(url) +} diff --git a/v2/internal/frontend/desktop/windows/dialog.go b/v2/internal/frontend/desktop/windows/dialog.go new file mode 100644 index 000000000..eb7747223 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/dialog.go @@ -0,0 +1,153 @@ +//go:build windows + +package windows + +import ( + "github.com/leaanthony/go-common-file-dialog/cfd" + "github.com/wailsapp/wails/v2/internal/frontend" + "golang.org/x/sys/windows" + "syscall" +) + +// OpenDirectoryDialog prompts the user to select a directory +func (f *Frontend) OpenDirectoryDialog(options frontend.OpenDialogOptions) (string, error) { + config := cfd.DialogConfig{ + Title: options.Title, + Role: "PickFolder", + Folder: options.DefaultDirectory, + } + thisDialog, err := cfd.NewSelectFolderDialog(config) + if err != nil { + return "", err + } + thisDialog.SetParentWindowHandle(f.mainWindow.Handle()) + defer func(thisDialog cfd.SelectFolderDialog) { + err := thisDialog.Release() + if err != nil { + println("ERROR: Unable to release dialog:", err.Error()) + } + }(thisDialog) + result, err := thisDialog.ShowAndGetResult() + if err != nil && err != cfd.ErrorCancelled { + return "", err + } + return result, nil +} + +// OpenFileDialog prompts the user to select a file +func (f *Frontend) OpenFileDialog(options frontend.OpenDialogOptions) (string, error) { + config := cfd.DialogConfig{ + Folder: options.DefaultDirectory, + FileFilters: convertFilters(options.Filters), + FileName: options.DefaultFilename, + Title: options.Title, + } + thisdialog, err := cfd.NewOpenFileDialog(config) + if err != nil { + return "", err + } + thisdialog.SetParentWindowHandle(f.mainWindow.Handle()) + defer func(thisdialog cfd.OpenFileDialog) { + err := thisdialog.Release() + if err != nil { + println("ERROR: Unable to release dialog:", err.Error()) + } + }(thisdialog) + result, err := thisdialog.ShowAndGetResult() + if err != nil && err != cfd.ErrorCancelled { + return "", err + } + return result, nil +} + +// OpenMultipleFilesDialog prompts the user to select a file +func (f *Frontend) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) { + config := cfd.DialogConfig{ + Title: dialogOptions.Title, + Role: "OpenMultipleFiles", + FileFilters: convertFilters(dialogOptions.Filters), + FileName: dialogOptions.DefaultFilename, + Folder: dialogOptions.DefaultDirectory, + } + thisdialog, err := cfd.NewOpenMultipleFilesDialog(config) + if err != nil { + return nil, err + } + thisdialog.SetParentWindowHandle(f.mainWindow.Handle()) + defer func(thisdialog cfd.OpenMultipleFilesDialog) { + err := thisdialog.Release() + if err != nil { + println("ERROR: Unable to release dialog:", err.Error()) + } + }(thisdialog) + result, err := thisdialog.ShowAndGetResults() + if err != nil && err != cfd.ErrorCancelled { + return nil, err + } + return result, nil +} + +// SaveFileDialog prompts the user to select a file +func (f *Frontend) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) { + saveDialog, err := cfd.NewSaveFileDialog(cfd.DialogConfig{ + Title: dialogOptions.Title, + Role: "SaveFile", + FileFilters: convertFilters(dialogOptions.Filters), + FileName: dialogOptions.DefaultFilename, + Folder: dialogOptions.DefaultDirectory, + }) + if err != nil { + return "", err + } + saveDialog.SetParentWindowHandle(f.mainWindow.Handle()) + err = saveDialog.Show() + if err != nil { + return "", err + } + result, err := saveDialog.GetResult() + if err != nil && err != cfd.ErrorCancelled { + return "", err + } + return result, nil +} + +// MessageDialog show a message dialog to the user +func (f *Frontend) MessageDialog(options frontend.MessageDialogOptions) (string, error) { + + title, err := syscall.UTF16PtrFromString(options.Title) + if err != nil { + return "", err + } + message, err := syscall.UTF16PtrFromString(options.Message) + if err != nil { + return "", err + } + var flags uint32 + switch options.Type { + case frontend.InfoDialog: + flags = windows.MB_OK | windows.MB_ICONINFORMATION + case frontend.ErrorDialog: + flags = windows.MB_ICONERROR | windows.MB_OK + case frontend.QuestionDialog: + flags = windows.MB_YESNO + case frontend.WarningDialog: + flags = windows.MB_OK | windows.MB_ICONWARNING + } + + button, _ := windows.MessageBox(windows.HWND(f.mainWindow.Handle()), message, title, flags|windows.MB_SYSTEMMODAL) + // This maps MessageBox return values to strings + responses := []string{"", "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "", "", "Try Again", "Continue"} + result := "Error" + if int(button) < len(responses) { + result = responses[button] + } + return result, nil +} + +func convertFilters(filters []frontend.FileFilter) []cfd.FileFilter { + var result []cfd.FileFilter + for _, filter := range filters { + result = append(result, cfd.FileFilter(filter)) + } + return result +} diff --git a/v2/internal/frontend/desktop/windows/frontend.go b/v2/internal/frontend/desktop/windows/frontend.go new file mode 100644 index 000000000..2f551cd15 --- /dev/null +++ b/v2/internal/frontend/desktop/windows/frontend.go @@ -0,0 +1,431 @@ +//go:build windows +// +build windows + +package windows + +import ( + "context" + "encoding/json" + "fmt" + "log" + "runtime" + "strconv" + "strings" + "text/template" + + "github.com/leaanthony/go-webview2/pkg/edge" + "github.com/leaanthony/winc" + "github.com/leaanthony/winc/w32" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/frontend/assetserver" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options" +) + +type Frontend struct { + + // Context + ctx context.Context + + frontendOptions *options.App + logger *logger.Logger + chromium *edge.Chromium + debug bool + + // Assets + assets *assetserver.DesktopAssetServer + startURL string + + // main window handle + mainWindow *Window + minWidth, minHeight, maxWidth, maxHeight int + bindings *binding.Bindings + dispatcher frontend.Dispatcher + servingFromDisk bool +} + +func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend { + + result := &Frontend{ + frontendOptions: appoptions, + logger: myLogger, + bindings: appBindings, + dispatcher: dispatcher, + ctx: ctx, + minHeight: appoptions.MinHeight, + minWidth: appoptions.MinWidth, + maxHeight: appoptions.MaxHeight, + maxWidth: appoptions.MaxWidth, + startURL: "file://wails/", + } + + bindingsJSON, err := appBindings.ToJSON() + if err != nil { + log.Fatal(err) + } + + _devServerURL := ctx.Value("devserverurl") + if _devServerURL != nil { + devServerURL := _devServerURL.(string) + if len(devServerURL) > 0 && devServerURL != "http://localhost:34115" { + result.startURL = devServerURL + return result + } + } + + // Check if we have been given a directory to serve assets from. + // If so, this means we are in dev mode and are serving assets off disk. + // We indicate this through the `servingFromDisk` flag to ensure requests + // aren't cached by WebView2 in dev mode + + _assetdir := ctx.Value("assetdir") + if _assetdir != nil { + result.servingFromDisk = true + } + + assets, err := assetserver.NewDesktopAssetServer(ctx, appoptions.Assets, bindingsJSON) + if err != nil { + log.Fatal(err) + } + result.assets = assets + + return result +} + +func (f *Frontend) WindowReload() { + f.ExecJS("runtime.WindowReload();") +} + +func (f *Frontend) Run(ctx context.Context) error { + + f.ctx = context.WithValue(ctx, "frontend", f) + + mainWindow := NewWindow(nil, f.frontendOptions) + f.mainWindow = mainWindow + + var _debug = ctx.Value("debug") + if _debug != nil { + f.debug = _debug.(bool) + } + + f.WindowCenter() + f.setupChromium() + + mainWindow.OnSize().Bind(func(arg *winc.Event) { + f.chromium.Resize() + }) + + mainWindow.OnClose().Bind(func(arg *winc.Event) { + if f.frontendOptions.HideWindowOnClose { + f.WindowHide() + } else { + f.Quit() + } + }) + + go func() { + if f.frontendOptions.OnStartup != nil { + f.frontendOptions.OnStartup(f.ctx) + } + }() + + if f.frontendOptions.Fullscreen { + mainWindow.Fullscreen() + } + + mainWindow.Run() + mainWindow.Close() + return nil +} + +func (f *Frontend) WindowCenter() { + runtime.LockOSThread() + f.mainWindow.Center() +} + +func (f *Frontend) WindowSetPos(x, y int) { + runtime.LockOSThread() + f.mainWindow.SetPos(x, y) +} +func (f *Frontend) WindowGetPos() (int, int) { + runtime.LockOSThread() + return f.mainWindow.Pos() +} + +func (f *Frontend) WindowSetSize(width, height int) { + runtime.LockOSThread() + f.mainWindow.SetSize(width, height) +} + +func (f *Frontend) WindowGetSize() (int, int) { + runtime.LockOSThread() + return f.mainWindow.Size() +} + +func (f *Frontend) WindowSetTitle(title string) { + runtime.LockOSThread() + f.mainWindow.SetText(title) +} + +func (f *Frontend) WindowFullscreen() { + runtime.LockOSThread() + f.mainWindow.SetMaxSize(0, 0) + f.mainWindow.SetMinSize(0, 0) + f.mainWindow.Fullscreen() +} + +func (f *Frontend) WindowUnFullscreen() { + runtime.LockOSThread() + f.mainWindow.UnFullscreen() + f.mainWindow.SetMaxSize(f.maxWidth, f.maxHeight) + f.mainWindow.SetMinSize(f.minWidth, f.minHeight) +} + +func (f *Frontend) WindowShow() { + runtime.LockOSThread() + f.mainWindow.Show() +} + +func (f *Frontend) WindowHide() { + runtime.LockOSThread() + f.mainWindow.Hide() +} +func (f *Frontend) WindowMaximise() { + runtime.LockOSThread() + f.mainWindow.Maximise() +} +func (f *Frontend) WindowUnmaximise() { + runtime.LockOSThread() + f.mainWindow.Restore() +} +func (f *Frontend) WindowMinimise() { + runtime.LockOSThread() + f.mainWindow.Minimise() +} +func (f *Frontend) WindowUnminimise() { + runtime.LockOSThread() + f.mainWindow.Restore() +} + +func (f *Frontend) WindowSetMinSize(width int, height int) { + runtime.LockOSThread() + f.minWidth = width + f.minHeight = height + f.mainWindow.SetMinSize(width, height) +} +func (f *Frontend) WindowSetMaxSize(width int, height int) { + runtime.LockOSThread() + f.maxWidth = width + f.maxHeight = height + f.mainWindow.SetMaxSize(width, height) +} + +func (f *Frontend) WindowSetRGBA(col *options.RGBA) { + runtime.LockOSThread() + if col == nil { + return + } + + f.mainWindow.Dispatch(func() { + controller := f.chromium.GetController() + controller2 := controller.GetICoreWebView2Controller2() + + backgroundCol := edge.COREWEBVIEW2_COLOR{ + A: col.A, + R: col.R, + G: col.G, + B: col.B, + } + + // Webview2 only has 0 and 255 as valid values. + if backgroundCol.A > 0 && backgroundCol.A < 255 { + backgroundCol.A = 255 + } + + if f.frontendOptions.Windows != nil && f.frontendOptions.Windows.WebviewIsTransparent { + backgroundCol.A = 0 + } + + err := controller2.PutDefaultBackgroundColor(backgroundCol) + if err != nil { + log.Fatal(err) + } + }) +} + +func (f *Frontend) Quit() { + winc.Exit() +} + +func (f *Frontend) setupChromium() { + chromium := edge.NewChromium() + f.chromium = chromium + chromium.MessageCallback = f.processMessage + chromium.WebResourceRequestedCallback = f.processRequest + chromium.NavigationCompletedCallback = f.navigationCompleted + chromium.AcceleratorKeyCallback = func(vkey uint) bool { + w32.PostMessage(f.mainWindow.Handle(), w32.WM_KEYDOWN, uintptr(vkey), 0) + return false + } + chromium.Embed(f.mainWindow.Handle()) + chromium.Resize() + settings, err := chromium.GetSettings() + if err != nil { + log.Fatal(err) + } + err = settings.PutAreDefaultContextMenusEnabled(f.debug) + if err != nil { + log.Fatal(err) + } + err = settings.PutAreDevToolsEnabled(f.debug) + if err != nil { + log.Fatal(err) + } + err = settings.PutIsZoomControlEnabled(false) + if err != nil { + log.Fatal(err) + } + err = settings.PutIsStatusBarEnabled(false) + if err != nil { + log.Fatal(err) + } + err = settings.PutAreBrowserAcceleratorKeysEnabled(false) + if err != nil { + log.Fatal(err) + } + err = settings.PutIsSwipeNavigationEnabled(false) + if err != nil { + log.Fatal(err) + } + + // Set background colour + f.WindowSetRGBA(f.frontendOptions.RGBA) + + chromium.SetGlobalPermission(edge.CoreWebView2PermissionStateAllow) + chromium.AddWebResourceRequestedFilter("*", edge.COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL) + chromium.Navigate(f.startURL) +} + +type EventNotify struct { + Name string `json:"name"` + Data []interface{} `json:"data"` +} + +func (f *Frontend) Notify(name string, data ...interface{}) { + notification := EventNotify{ + Name: name, + Data: data, + } + payload, err := json.Marshal(notification) + if err != nil { + f.logger.Error(err.Error()) + return + } + f.ExecJS(`window.wails.EventsNotify('` + template.JSEscapeString(string(payload)) + `');`) +} + +func (f *Frontend) processRequest(req *edge.ICoreWebView2WebResourceRequest, args *edge.ICoreWebView2WebResourceRequestedEventArgs) { + //Get the request + uri, _ := req.GetUri() + + // Translate URI + uri = strings.TrimPrefix(uri, "file://wails") + if !strings.HasPrefix(uri, "/") { + return + } + + // Load file from asset store + content, mimeType, err := f.assets.Load(uri) + if err != nil { + return + } + + env := f.chromium.Environment() + headers := "Content-Type: " + mimeType + if f.servingFromDisk { + headers += "\nPragma: no-cache" + } + response, err := env.CreateWebResourceResponse(content, 200, "OK", headers) + if err != nil { + return + } + // Send response back + err = args.PutResponse(response) + if err != nil { + return + } + return +} + +func (f *Frontend) processMessage(message string) { + if message == "drag" { + if !f.mainWindow.IsFullScreen() { + err := f.startDrag() + if err != nil { + f.logger.Error(err.Error()) + } + } + return + } + result, err := f.dispatcher.ProcessMessage(message, f) + if err != nil { + f.logger.Error(err.Error()) + f.Callback(result) + return + } + if result == "" { + return + } + + switch result[0] { + case 'c': + // Callback from a method call + f.Callback(result[1:]) + default: + f.logger.Info("Unknown message returned from dispatcher: %+v", result) + } +} + +func (f *Frontend) Callback(message string) { + f.mainWindow.Dispatch(func() { + f.chromium.Eval(`window.wails.Callback(` + strconv.Quote(message) + `);`) + }) +} + +func (f *Frontend) startDrag() error { + if !w32.ReleaseCapture() { + return fmt.Errorf("unable to release mouse capture") + } + w32.SendMessage(f.mainWindow.Handle(), w32.WM_NCLBUTTONDOWN, w32.HTCAPTION, 0) + return nil +} + +func (f *Frontend) ExecJS(js string) { + f.mainWindow.Dispatch(func() { + f.chromium.Eval(js) + }) +} + +func (f *Frontend) navigationCompleted(sender *edge.ICoreWebView2, args *edge.ICoreWebView2NavigationCompletedEventArgs) { + if f.frontendOptions.OnDomReady != nil { + go f.frontendOptions.OnDomReady(f.ctx) + } + + // If you want to start hidden, return + if f.frontendOptions.StartHidden { + return + } + + // Hack to make it visible: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077#issuecomment-825375026 + err := f.chromium.Hide() + if err != nil { + log.Fatal(err) + } + err = f.chromium.Show() + if err != nil { + log.Fatal(err) + } + f.mainWindow.Show() + +} diff --git a/v2/internal/frontend/desktop/windows/keys.go b/v2/internal/frontend/desktop/windows/keys.go new file mode 100644 index 000000000..8c2c39bde --- /dev/null +++ b/v2/internal/frontend/desktop/windows/keys.go @@ -0,0 +1,202 @@ +//go:build windows + +package windows + +import ( + "github.com/leaanthony/winc" + "github.com/wailsapp/wails/v2/pkg/menu/keys" + "strings" +) + +var ModifierMap = map[keys.Modifier]winc.Modifiers{ + keys.ShiftKey: winc.ModShift, + keys.ControlKey: winc.ModControl, + keys.OptionOrAltKey: winc.ModAlt, + keys.CmdOrCtrlKey: winc.ModControl, +} + +func acceleratorToWincShortcut(accelerator *keys.Accelerator) winc.Shortcut { + + if accelerator == nil { + return winc.NoShortcut + } + inKey := strings.ToUpper(accelerator.Key) + key, exists := keyMap[inKey] + if !exists { + return winc.NoShortcut + } + var modifiers winc.Modifiers + if _, exists := shiftMap[inKey]; exists { + modifiers = winc.ModShift + } + for _, mod := range accelerator.Modifiers { + modifiers |= ModifierMap[mod] + } + return winc.Shortcut{ + Modifiers: modifiers, + Key: key, + } +} + +var shiftMap = map[string]struct{}{ + "~": {}, + ")": {}, + "!": {}, + "@": {}, + "#": {}, + "$": {}, + "%": {}, + "^": {}, + "&": {}, + "*": {}, + "(": {}, + "_": {}, + "PLUS": {}, + "<": {}, + ">": {}, + "?": {}, + ":": {}, + `"`: {}, + "{": {}, + "}": {}, + "|": {}, +} + +var keyMap = map[string]winc.Key{ + "0": winc.Key0, + "1": winc.Key1, + "2": winc.Key2, + "3": winc.Key3, + "4": winc.Key4, + "5": winc.Key5, + "6": winc.Key6, + "7": winc.Key7, + "8": winc.Key8, + "9": winc.Key9, + "A": winc.KeyA, + "B": winc.KeyB, + "C": winc.KeyC, + "D": winc.KeyD, + "E": winc.KeyE, + "F": winc.KeyF, + "G": winc.KeyG, + "H": winc.KeyH, + "I": winc.KeyI, + "J": winc.KeyJ, + "K": winc.KeyK, + "L": winc.KeyL, + "M": winc.KeyM, + "N": winc.KeyN, + "O": winc.KeyO, + "P": winc.KeyP, + "Q": winc.KeyQ, + "R": winc.KeyR, + "S": winc.KeyS, + "T": winc.KeyT, + "U": winc.KeyU, + "V": winc.KeyV, + "W": winc.KeyW, + "X": winc.KeyX, + "Y": winc.KeyY, + "Z": winc.KeyZ, + "F1": winc.KeyF1, + "F2": winc.KeyF2, + "F3": winc.KeyF3, + "F4": winc.KeyF4, + "F5": winc.KeyF5, + "F6": winc.KeyF6, + "F7": winc.KeyF7, + "F8": winc.KeyF8, + "F9": winc.KeyF9, + "F10": winc.KeyF10, + "F11": winc.KeyF11, + "F12": winc.KeyF12, + "F13": winc.KeyF13, + "F14": winc.KeyF14, + "F15": winc.KeyF15, + "F16": winc.KeyF16, + "F17": winc.KeyF17, + "F18": winc.KeyF18, + "F19": winc.KeyF19, + "F20": winc.KeyF20, + "F21": winc.KeyF21, + "F22": winc.KeyF22, + "F23": winc.KeyF23, + "F24": winc.KeyF24, + + "`": winc.KeyOEM3, + ",": winc.KeyOEMComma, + ".": winc.KeyOEMPeriod, + "/": winc.KeyOEM2, + ";": winc.KeyOEM1, + "'": winc.KeyOEM7, + "[": winc.KeyOEM4, + "]": winc.KeyOEM6, + `\`: winc.KeyOEM5, + + "~": winc.KeyOEM3, // + ")": winc.Key0, + "!": winc.Key1, + "@": winc.Key2, + "#": winc.Key3, + "$": winc.Key4, + "%": winc.Key5, + "^": winc.Key6, + "&": winc.Key7, + "*": winc.Key8, + "(": winc.Key9, + "_": winc.KeyOEMMinus, + "PLUS": winc.KeyOEMPlus, + "<": winc.KeyOEMComma, + ">": winc.KeyOEMPeriod, + "?": winc.KeyOEM2, + ":": winc.KeyOEM1, + `"`: winc.KeyOEM7, + "{": winc.KeyOEM4, + "}": winc.KeyOEM6, + "|": winc.KeyOEM5, + + "SPACE": winc.KeySpace, + "TAB": winc.KeyTab, + "CAPSLOCK": winc.KeyCapital, + "NUMLOCK": winc.KeyNumlock, + "SCROLLLOCK": winc.KeyScroll, + "BACKSPACE": winc.KeyBack, + "DELETE": winc.KeyDelete, + "INSERT": winc.KeyInsert, + "RETURN": winc.KeyReturn, + "ENTER": winc.KeyReturn, + "UP": winc.KeyUp, + "DOWN": winc.KeyDown, + "LEFT": winc.KeyLeft, + "RIGHT": winc.KeyRight, + "HOME": winc.KeyHome, + "END": winc.KeyEnd, + "PAGEUP": winc.KeyPrior, + "PAGEDOWN": winc.KeyNext, + "ESCAPE": winc.KeyEscape, + "ESC": winc.KeyEscape, + "VOLUMEUP": winc.KeyVolumeUp, + "VOLUMEDOWN": winc.KeyVolumeDown, + "VOLUMEMUTE": winc.KeyVolumeMute, + "MEDIANEXTTRACK": winc.KeyMediaNextTrack, + "MEDIAPREVIOUSTRACK": winc.KeyMediaPrevTrack, + "MEDIASTOP": winc.KeyMediaStop, + "MEDIAPLAYPAUSE": winc.KeyMediaPlayPause, + "PRINTSCREEN": winc.KeyPrint, + "NUM0": winc.KeyNumpad0, + "NUM1": winc.KeyNumpad1, + "NUM2": winc.KeyNumpad2, + "NUM3": winc.KeyNumpad3, + "NUM4": winc.KeyNumpad4, + "NUM5": winc.KeyNumpad5, + "NUM6": winc.KeyNumpad6, + "NUM7": winc.KeyNumpad7, + "NUM8": winc.KeyNumpad8, + "NUM9": winc.KeyNumpad9, + "nummult": winc.KeyMultiply, + "numadd": winc.KeyAdd, + "numsub": winc.KeySubtract, + "numdec": winc.KeyDecimal, + "numdiv": winc.KeyDivide, +} diff --git a/v2/internal/frontend/desktop/windows/menu.go b/v2/internal/frontend/desktop/windows/menu.go new file mode 100644 index 000000000..82f5421ed --- /dev/null +++ b/v2/internal/frontend/desktop/windows/menu.go @@ -0,0 +1,129 @@ +//go:build windows + +package windows + +import ( + "github.com/leaanthony/winc" + "github.com/wailsapp/wails/v2/pkg/menu" +) + +var checkboxMap = map[*menu.MenuItem][]*winc.MenuItem{} +var radioGroupMap = map[*menu.MenuItem][]*winc.MenuItem{} + +func toggleCheckBox(menuItem *menu.MenuItem) { + menuItem.Checked = !menuItem.Checked + for _, wincMenu := range checkboxMap[menuItem] { + wincMenu.SetChecked(menuItem.Checked) + } +} + +func addCheckBoxToMap(menuItem *menu.MenuItem, wincMenuItem *winc.MenuItem) { + if checkboxMap[menuItem] == nil { + checkboxMap[menuItem] = []*winc.MenuItem{} + } + checkboxMap[menuItem] = append(checkboxMap[menuItem], wincMenuItem) +} + +func toggleRadioItem(menuItem *menu.MenuItem) { + menuItem.Checked = !menuItem.Checked + for _, wincMenu := range radioGroupMap[menuItem] { + wincMenu.SetChecked(menuItem.Checked) + } +} + +func addRadioItemToMap(menuItem *menu.MenuItem, wincMenuItem *winc.MenuItem) { + if radioGroupMap[menuItem] == nil { + radioGroupMap[menuItem] = []*winc.MenuItem{} + } + radioGroupMap[menuItem] = append(radioGroupMap[menuItem], wincMenuItem) +} + +func (w *Window) SetApplicationMenu(menu *menu.Menu) { + w.applicationMenu = menu + processMenu(w, menu) +} + +func processMenu(window *Window, menu *menu.Menu) { + mainMenu := window.NewMenu() + for _, menuItem := range menu.Items { + submenu := mainMenu.AddSubMenu(menuItem.Label) + for _, menuItem := range menuItem.SubMenu.Items { + processMenuItem(submenu, menuItem) + } + } + mainMenu.Show() +} + +func processMenuItem(parent *winc.MenuItem, menuItem *menu.MenuItem) { + if menuItem.Hidden { + return + } + switch menuItem.Type { + case menu.SeparatorType: + parent.AddSeparator() + case menu.TextType: + shortcut := acceleratorToWincShortcut(menuItem.Accelerator) + newItem := parent.AddItem(menuItem.Label, shortcut) + //if menuItem.Tooltip != "" { + // newItem.SetToolTip(menuItem.Tooltip) + //} + if menuItem.Click != nil { + newItem.OnClick().Bind(func(e *winc.Event) { + menuItem.Click(&menu.CallbackData{ + MenuItem: menuItem, + }) + }) + } + newItem.SetEnabled(!menuItem.Disabled) + + case menu.CheckboxType: + shortcut := acceleratorToWincShortcut(menuItem.Accelerator) + newItem := parent.AddItem(menuItem.Label, shortcut) + newItem.SetCheckable(true) + newItem.SetChecked(menuItem.Checked) + //if menuItem.Tooltip != "" { + // newItem.SetToolTip(menuItem.Tooltip) + //} + if menuItem.Click != nil { + newItem.OnClick().Bind(func(e *winc.Event) { + toggleCheckBox(menuItem) + menuItem.Click(&menu.CallbackData{ + MenuItem: menuItem, + }) + }) + } + newItem.SetEnabled(!menuItem.Disabled) + addCheckBoxToMap(menuItem, newItem) + case menu.RadioType: + shortcut := acceleratorToWincShortcut(menuItem.Accelerator) + newItem := parent.AddItemRadio(menuItem.Label, shortcut) + newItem.SetCheckable(true) + newItem.SetChecked(menuItem.Checked) + //if menuItem.Tooltip != "" { + // newItem.SetToolTip(menuItem.Tooltip) + //} + if menuItem.Click != nil { + newItem.OnClick().Bind(func(e *winc.Event) { + toggleRadioItem(menuItem) + menuItem.Click(&menu.CallbackData{ + MenuItem: menuItem, + }) + }) + } + newItem.SetEnabled(!menuItem.Disabled) + addRadioItemToMap(menuItem, newItem) + case menu.SubmenuType: + submenu := parent.AddSubMenu(menuItem.Label) + for _, menuItem := range menuItem.SubMenu.Items { + processMenuItem(submenu, menuItem) + } + } +} + +func (f *Frontend) MenuSetApplicationMenu(menu *menu.Menu) { + f.mainWindow.SetApplicationMenu(menu) +} + +func (f *Frontend) MenuUpdateApplicationMenu() { + processMenu(f.mainWindow, f.mainWindow.applicationMenu) +} diff --git a/v2/internal/frontend/desktop/windows/window.go b/v2/internal/frontend/desktop/windows/window.go new file mode 100644 index 000000000..8087e37fa --- /dev/null +++ b/v2/internal/frontend/desktop/windows/window.go @@ -0,0 +1,117 @@ +//go:build windows + +package windows + +import ( + "github.com/leaanthony/winc" + "github.com/leaanthony/winc/w32" + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/options" + "sync" +) + +type Window struct { + winc.Form + frontendOptions *options.App + applicationMenu *menu.Menu + m sync.Mutex + dispatchq []func() +} + +func NewWindow(parent winc.Controller, options *options.App) *Window { + result := new(Window) + result.frontendOptions = options + result.SetIsForm(true) + + var exStyle int + if options.Windows != nil { + exStyle = w32.WS_EX_CONTROLPARENT | w32.WS_EX_APPWINDOW + if options.Windows.WindowIsTranslucent { + exStyle |= w32.WS_EX_NOREDIRECTIONBITMAP + } + } + if options.AlwaysOnTop { + exStyle |= w32.WS_EX_TOPMOST + } + + var dwStyle = w32.WS_OVERLAPPEDWINDOW + if options.Frameless { + dwStyle = w32.WS_POPUP + } + + winc.RegClassOnlyOnce("wailsWindow") + result.SetHandle(winc.CreateWindow("wailsWindow", parent, uint(exStyle), uint(dwStyle))) + result.SetParent(parent) + + loadIcon := true + if options.Windows != nil && options.Windows.DisableWindowIcon == true { + loadIcon = false + } + if loadIcon { + if ico, err := winc.NewIconFromResource(winc.GetAppInstance(), uint16(winc.AppIconID)); err == nil { + result.SetIcon(0, ico) + } + } + + result.SetSize(options.Width, options.Height) + result.SetText(options.Title) + if options.Frameless == false && !options.Fullscreen { + result.EnableMaxButton(!options.DisableResize) + result.EnableSizable(!options.DisableResize) + result.SetMinSize(options.MinWidth, options.MinHeight) + result.SetMaxSize(options.MaxWidth, options.MaxHeight) + } + + if options.Windows != nil { + if options.Windows.WindowIsTranslucent { + result.SetTranslucentBackground() + } + + if options.Windows.DisableWindowIcon { + result.DisableIcon() + } + } + + // Dlg forces display of focus rectangles, as soon as the user starts to type. + w32.SendMessage(result.Handle(), w32.WM_CHANGEUISTATE, w32.UIS_INITIALIZE, 0) + winc.RegMsgHandler(result) + + result.SetFont(winc.DefaultFont) + + if options.Menu != nil { + result.SetApplicationMenu(options.Menu) + } + + return result +} + +func (w *Window) Run() int { + var m w32.MSG + + for w32.GetMessage(&m, 0, 0, 0) != 0 { + if m.Message == w32.WM_APP { + // Credit: https://github.com/jchv/go-webview2 + w.m.Lock() + q := append([]func(){}, w.dispatchq...) + w.dispatchq = []func(){} + w.m.Unlock() + for _, v := range q { + v() + } + } + if !w.PreTranslateMessage(&m) { + w32.TranslateMessage(&m) + w32.DispatchMessage(&m) + } + } + + w32.GdiplusShutdown() + return int(m.WParam) +} + +func (w *Window) Dispatch(f func()) { + w.m.Lock() + w.dispatchq = append(w.dispatchq, f) + w.m.Unlock() + w32.PostMainThreadMessage(w32.WM_APP, 0, 0) +} diff --git a/v2/internal/frontend/devserver/devserver.go b/v2/internal/frontend/devserver/devserver.go new file mode 100644 index 000000000..e73828c5d --- /dev/null +++ b/v2/internal/frontend/devserver/devserver.go @@ -0,0 +1,394 @@ +//go:build dev + +// Package devserver provides a web-based frontend so that +// it is possible to run a Wails app in a browsers. +package devserver + +import ( + "context" + "encoding/json" + "fmt" + "io/fs" + "log" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/websocket/v2" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/frontend/assetserver" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/menumanager" + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/options" +) + +type DevWebServer struct { + server *fiber.App + ctx context.Context + appoptions *options.App + logger *logger.Logger + appBindings *binding.Bindings + dispatcher frontend.Dispatcher + assetServer *assetserver.BrowserAssetServer + socketMutex sync.Mutex + websocketClients map[*websocket.Conn]*sync.Mutex + menuManager *menumanager.Manager + starttime string + + // Desktop frontend + desktopFrontend frontend.Frontend +} + +func (d *DevWebServer) WindowReload() { + d.broadcast("reload") +} + +func (d *DevWebServer) Run(ctx context.Context) error { + d.ctx = ctx + + d.server.Get("/wails/reload", func(fctx *fiber.Ctx) error { + d.WindowReload() + d.desktopFrontend.WindowReload() + return nil + }) + + d.server.Get("/wails/ipc", websocket.New(func(c *websocket.Conn) { + d.newWebsocketSession(c) + locker := d.websocketClients[c] + // websocket.Conn bindings https://pkg.go.dev/github.com/fasthttp/websocket?tab=doc#pkg-index + var ( + mt int + msg []byte + err error + ) + + for { + if mt, msg, err = c.ReadMessage(); err != nil { + break + } + // We do not support drag in browsers + if string(msg) == "drag" { + continue + } + + // Notify the other browsers of "EventEmit" + if len(msg) > 2 && strings.HasPrefix(string(msg), "EE") { + d.notifyExcludingSender(msg, c) + } + + // Send the message to dispatch to the frontend + result, err := d.dispatcher.ProcessMessage(string(msg), d) + if err != nil { + d.logger.Error(err.Error()) + } + if result != "" { + locker.Lock() + if err = c.WriteMessage(mt, []byte(result)); err != nil { + locker.Unlock() + break + } + locker.Unlock() + } + + } + })) + + _devServerURL := ctx.Value("devserverurl") + if _devServerURL == "http://localhost:34115" { + // Setup internal dev server + _assetdir := ctx.Value("assetdir") + if _assetdir == nil { + return fmt.Errorf("no assetdir provided") + } + if _assetdir != nil { + assetdir := _assetdir.(string) + bindingsJSON, err := d.appBindings.ToJSON() + if err != nil { + log.Fatal(err) + } + d.assetServer, err = assetserver.NewBrowserAssetServer(assetdir, bindingsJSON, d.appoptions) + if err != nil { + log.Fatal(err) + } + absdir, err := filepath.Abs(assetdir) + if err != nil { + return err + } + d.LogDebug("Serving assets from: %s", absdir) + } + + d.server.Get("*", d.loadAsset) + + // Start server + go func(server *fiber.App, log *logger.Logger) { + err := server.Listen("localhost:34115") + if err != nil { + log.Error(err.Error()) + } + d.LogDebug("Shutdown completed") + }(d.server, d.logger) + + d.LogDebug("Serving application at http://localhost:34115") + + defer func() { + err := d.server.Shutdown() + if err != nil { + d.logger.Error(err.Error()) + } + }() + } + + // Launch desktop app + err := d.desktopFrontend.Run(ctx) + d.LogDebug("Starting shutdown") + + return err +} + +func (d *DevWebServer) Quit() { + d.desktopFrontend.Quit() +} + +func (d *DevWebServer) OpenFileDialog(dialogOptions frontend.OpenDialogOptions) (string, error) { + return d.desktopFrontend.OpenFileDialog(dialogOptions) +} + +func (d *DevWebServer) OpenMultipleFilesDialog(dialogOptions frontend.OpenDialogOptions) ([]string, error) { + return d.OpenMultipleFilesDialog(dialogOptions) +} + +func (d *DevWebServer) OpenDirectoryDialog(dialogOptions frontend.OpenDialogOptions) (string, error) { + return d.OpenDirectoryDialog(dialogOptions) +} + +func (d *DevWebServer) SaveFileDialog(dialogOptions frontend.SaveDialogOptions) (string, error) { + return d.desktopFrontend.SaveFileDialog(dialogOptions) +} + +func (d *DevWebServer) MessageDialog(dialogOptions frontend.MessageDialogOptions) (string, error) { + return d.desktopFrontend.MessageDialog(dialogOptions) +} + +func (d *DevWebServer) WindowSetTitle(title string) { + d.desktopFrontend.WindowSetTitle(title) +} + +func (d *DevWebServer) WindowShow() { + d.desktopFrontend.WindowShow() +} + +func (d *DevWebServer) WindowHide() { + d.desktopFrontend.WindowHide() +} + +func (d *DevWebServer) WindowCenter() { + d.desktopFrontend.WindowCenter() +} + +func (d *DevWebServer) WindowMaximise() { + d.desktopFrontend.WindowMaximise() +} + +func (d *DevWebServer) WindowUnmaximise() { + d.desktopFrontend.WindowUnmaximise() +} + +func (d *DevWebServer) WindowMinimise() { + d.desktopFrontend.WindowMinimise() +} + +func (d *DevWebServer) WindowUnminimise() { + d.desktopFrontend.WindowUnminimise() +} + +func (d *DevWebServer) WindowSetPos(x int, y int) { + d.desktopFrontend.WindowSetPos(x, y) +} + +func (d *DevWebServer) WindowGetPos() (int, int) { + return d.desktopFrontend.WindowGetPos() +} + +func (d *DevWebServer) WindowSetSize(width int, height int) { + d.desktopFrontend.WindowSetSize(width, height) +} + +func (d *DevWebServer) WindowGetSize() (int, int) { + return d.desktopFrontend.WindowGetSize() +} + +func (d *DevWebServer) WindowSetMinSize(width int, height int) { + d.desktopFrontend.WindowSetMinSize(width, height) +} + +func (d *DevWebServer) WindowSetMaxSize(width int, height int) { + d.desktopFrontend.WindowSetMaxSize(width, height) +} + +func (d *DevWebServer) WindowFullscreen() { + d.desktopFrontend.WindowFullscreen() +} + +func (d *DevWebServer) WindowUnFullscreen() { + d.desktopFrontend.WindowUnFullscreen() +} + +func (d *DevWebServer) WindowSetRGBA(col *options.RGBA) { + d.desktopFrontend.WindowSetRGBA(col) +} + +func (d *DevWebServer) MenuSetApplicationMenu(menu *menu.Menu) { + d.desktopFrontend.MenuSetApplicationMenu(menu) +} + +func (d *DevWebServer) MenuUpdateApplicationMenu() { + d.desktopFrontend.MenuUpdateApplicationMenu() +} + +// BrowserOpenURL uses the system default browser to open the url +func (d *DevWebServer) BrowserOpenURL(url string) { + d.desktopFrontend.BrowserOpenURL(url) +} + +func (d *DevWebServer) Notify(name string, data ...interface{}) { + d.notify(name, data...) +} + +func (d *DevWebServer) loadAsset(ctx *fiber.Ctx) error { + data, mimetype, err := d.assetServer.Load(ctx.Path()) + if err != nil { + _, ok := err.(*fs.PathError) + if !ok { + return err + } + err := ctx.SendStatus(404) + if err != nil { + return err + } + return nil + } + err = ctx.SendStatus(200) + if err != nil { + return err + } + ctx.Set("Content-Type", mimetype) + err = ctx.Send(data) + if err != nil { + return err + } + return nil +} + +func (d *DevWebServer) LogDebug(message string, args ...interface{}) { + d.logger.Debug("[DevWebServer] "+message, args...) +} + +func (d *DevWebServer) newWebsocketSession(c *websocket.Conn) { + d.socketMutex.Lock() + defer d.socketMutex.Unlock() + c.SetCloseHandler(func(code int, text string) error { + d.socketMutex.Lock() + defer d.socketMutex.Unlock() + delete(d.websocketClients, c) + d.LogDebug(fmt.Sprintf("Websocket client %p disconnected", c)) + return nil + }) + d.websocketClients[c] = &sync.Mutex{} + d.LogDebug(fmt.Sprintf("Websocket client %p connected", c)) +} + +type EventNotify struct { + Name string `json:"name"` + Data []interface{} `json:"data"` +} + +func (d *DevWebServer) broadcast(message string) { + d.socketMutex.Lock() + defer d.socketMutex.Unlock() + for client, locker := range d.websocketClients { + go func() { + if client == nil { + d.logger.Error("Lost connection to websocket server") + return + } + locker.Lock() + err := client.WriteMessage(websocket.TextMessage, []byte(message)) + if err != nil { + locker.Unlock() + d.logger.Error(err.Error()) + return + } + locker.Unlock() + }() + } +} + +func (d *DevWebServer) notify(name string, data ...interface{}) { + // Notify + notification := EventNotify{ + Name: name, + Data: data, + } + payload, err := json.Marshal(notification) + if err != nil { + d.logger.Error(err.Error()) + return + } + d.broadcast("n" + string(payload)) +} + +func (d *DevWebServer) broadcastExcludingSender(message string, sender *websocket.Conn) { + d.socketMutex.Lock() + defer d.socketMutex.Unlock() + for client, locker := range d.websocketClients { + go func() { + if client == sender { + return + } + locker.Lock() + err := client.WriteMessage(websocket.TextMessage, []byte(message)) + if err != nil { + locker.Unlock() + d.logger.Error(err.Error()) + return + } + locker.Unlock() + }() + } +} + +func (d *DevWebServer) notifyExcludingSender(eventMessage []byte, sender *websocket.Conn) { + message := "n" + string(eventMessage[2:]) + d.broadcastExcludingSender(message, sender) + + var notifyMessage EventNotify + err := json.Unmarshal(eventMessage[2:], ¬ifyMessage) + if err != nil { + d.logger.Error(err.Error()) + return + } + d.desktopFrontend.Notify(notifyMessage.Name, notifyMessage.Data...) +} + +func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher, menuManager *menumanager.Manager, desktopFrontend frontend.Frontend) *DevWebServer { + result := &DevWebServer{ + ctx: ctx, + desktopFrontend: desktopFrontend, + appoptions: appoptions, + logger: myLogger, + appBindings: appBindings, + dispatcher: dispatcher, + server: fiber.New(fiber.Config{ + + ReadTimeout: time.Second * 5, + DisableStartupMessage: true, + }), + menuManager: menuManager, + websocketClients: make(map[*websocket.Conn]*sync.Mutex), + } + return result +} diff --git a/v2/internal/frontend/dispatcher.go b/v2/internal/frontend/dispatcher.go new file mode 100644 index 000000000..367f4cdc8 --- /dev/null +++ b/v2/internal/frontend/dispatcher.go @@ -0,0 +1,5 @@ +package frontend + +type Dispatcher interface { + ProcessMessage(message string, sender Frontend) (string, error) +} diff --git a/v2/internal/frontend/dispatcher/browser.go b/v2/internal/frontend/dispatcher/browser.go new file mode 100644 index 000000000..9ff02e8d3 --- /dev/null +++ b/v2/internal/frontend/dispatcher/browser.go @@ -0,0 +1,23 @@ +package dispatcher + +import ( + "errors" + + "github.com/wailsapp/wails/v2/internal/frontend" +) + +// processBrowserMessage processing browser messages +func (d *Dispatcher) processBrowserMessage(message string, sender frontend.Frontend) (string, error) { + if len(message) < 2 { + return "", errors.New("Invalid Browser Message: " + message) + } + switch message[1] { + case 'O': + url := message[3:] + go sender.BrowserOpenURL(url) + default: + d.log.Error("unknown Browser message: %s", message) + } + + return "", nil +} diff --git a/v2/internal/frontend/dispatcher/calls.go b/v2/internal/frontend/dispatcher/calls.go new file mode 100644 index 000000000..ef19c6030 --- /dev/null +++ b/v2/internal/frontend/dispatcher/calls.go @@ -0,0 +1,81 @@ +package dispatcher + +import ( + "encoding/json" + "fmt" + "github.com/wailsapp/wails/v2/internal/frontend" + "strings" +) + +type callMessage struct { + Name string `json:"name"` + Args []json.RawMessage `json:"args"` + CallbackID string `json:"callbackID"` +} + +func (d *Dispatcher) processCallMessage(message string, sender frontend.Frontend) (string, error) { + + var payload callMessage + err := json.Unmarshal([]byte(message[1:]), &payload) + if err != nil { + return "", err + } + + var result interface{} + + // Handle different calls + switch true { + case strings.HasPrefix(payload.Name, systemCallPrefix): + result, err = d.processSystemCall(payload, sender) + default: + // Lookup method + registeredMethod := d.bindingsDB.GetMethod(payload.Name) + + // Check we have it + if registeredMethod == nil { + return "", fmt.Errorf("method '%s' not registered", payload.Name) + } + + args, err2 := registeredMethod.ParseArgs(payload.Args) + if err2 != nil { + errmsg := fmt.Errorf("error parsing arguments: %s", err2.Error()) + result, _ := d.NewErrorCallback(errmsg.Error(), payload.CallbackID) + return result, errmsg + } + result, err = registeredMethod.Call(args) + } + + callbackMessage := &CallbackMessage{ + CallbackID: payload.CallbackID, + } + if err != nil { + callbackMessage.Err = err.Error() + } else { + callbackMessage.Result = result + } + messageData, err := json.Marshal(callbackMessage) + d.log.Trace("json call result data: %+v\n", string(messageData)) + if err != nil { + // what now? + d.log.Fatal(err.Error()) + } + + return "c" + string(messageData), nil +} + +// CallbackMessage defines a message that contains the result of a call +type CallbackMessage struct { + Result interface{} `json:"result"` + Err string `json:"error"` + CallbackID string `json:"callbackid"` +} + +func (d *Dispatcher) NewErrorCallback(message string, callbackID string) (string, error) { + result := &CallbackMessage{ + CallbackID: callbackID, + Err: message, + } + messageData, err := json.Marshal(result) + d.log.Trace("json call result data: %+v\n", string(messageData)) + return string(messageData), err +} diff --git a/v2/internal/frontend/dispatcher/dispatcher.go b/v2/internal/frontend/dispatcher/dispatcher.go new file mode 100644 index 000000000..a3091f41c --- /dev/null +++ b/v2/internal/frontend/dispatcher/dispatcher.go @@ -0,0 +1,47 @@ +package dispatcher + +import ( + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/logger" +) + +type Dispatcher struct { + log *logger.Logger + bindings *binding.Bindings + events frontend.Events + bindingsDB *binding.DB +} + +func NewDispatcher(log *logger.Logger, bindings *binding.Bindings, events frontend.Events) *Dispatcher { + return &Dispatcher{ + log: log, + bindings: bindings, + events: events, + bindingsDB: bindings.DB(), + } +} + +func (d *Dispatcher) ProcessMessage(message string, sender frontend.Frontend) (string, error) { + if message == "" { + return "", errors.New("No message to process") + } + switch message[0] { + case 'L': + return d.processLogMessage(message) + case 'E': + return d.processEventMessage(message, sender) + case 'C': + return d.processCallMessage(message, sender) + case 'W': + return d.processWindowMessage(message, sender) + case 'B': + return d.processBrowserMessage(message, sender) + case 'Q': + sender.Quit() + return "", nil + default: + return "", errors.New("Unknown message from front end: " + message) + } +} diff --git a/v2/internal/frontend/dispatcher/events.go b/v2/internal/frontend/dispatcher/events.go new file mode 100644 index 000000000..cb308b803 --- /dev/null +++ b/v2/internal/frontend/dispatcher/events.go @@ -0,0 +1,33 @@ +package dispatcher + +import ( + "encoding/json" + "errors" + "github.com/wailsapp/wails/v2/internal/frontend" +) + +type EventMessage struct { + Name string `json:"name"` + Data []interface{} `json:"data"` +} + +func (d *Dispatcher) processEventMessage(message string, sender frontend.Frontend) (string, error) { + if len(message) < 3 { + return "", errors.New("Invalid Event Message: " + message) + } + + switch message[1] { + case 'E': + var eventMessage EventMessage + err := json.Unmarshal([]byte(message[2:]), &eventMessage) + if err != nil { + return "", err + } + go d.events.Notify(sender, eventMessage.Name, eventMessage.Data) + case 'X': + eventName := message[2:] + go d.events.Off(eventName) + } + + return "", nil +} diff --git a/v2/internal/frontend/dispatcher/log.go b/v2/internal/frontend/dispatcher/log.go new file mode 100644 index 000000000..e42555397 --- /dev/null +++ b/v2/internal/frontend/dispatcher/log.go @@ -0,0 +1,49 @@ +package dispatcher + +import ( + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/internal/logger" + pkgLogger "github.com/wailsapp/wails/v2/pkg/logger" +) + +var logLevelMap = map[byte]logger.LogLevel{ + '1': pkgLogger.TRACE, + '2': pkgLogger.DEBUG, + '3': pkgLogger.INFO, + '4': pkgLogger.WARNING, + '5': pkgLogger.ERROR, +} + +func (d *Dispatcher) processLogMessage(message string) (string, error) { + if len(message) < 3 { + return "", errors.New("Invalid Log Message: " + message) + } + + messageText := message[2:] + + switch message[1] { + case 'T': + d.log.Trace(messageText) + case 'P': + d.log.Print(messageText) + case 'D': + d.log.Debug(messageText) + case 'I': + d.log.Info(messageText) + case 'W': + d.log.Warning(messageText) + case 'E': + d.log.Error(messageText) + case 'F': + d.log.Fatal(messageText) + case 'S': + loglevel, exists := logLevelMap[message[2]] + if !exists { + return "", errors.New("Invalid Set Log Level Message: " + message) + } + d.log.SetLogLevel(loglevel) + default: + return "", errors.New("Invalid Log Message: " + message) + } + return "", nil +} diff --git a/v2/internal/frontend/dispatcher/systemcalls.go b/v2/internal/frontend/dispatcher/systemcalls.go new file mode 100644 index 000000000..8d23d54bf --- /dev/null +++ b/v2/internal/frontend/dispatcher/systemcalls.go @@ -0,0 +1,38 @@ +package dispatcher + +import ( + "fmt" + "strings" + + "github.com/wailsapp/wails/v2/internal/frontend" +) + +const systemCallPrefix = ":wails:" + +type position struct { + X int `json:"x"` + Y int `json:"y"` +} + +type size struct { + W int `json:"w"` + H int `json:"h"` +} + +func (d *Dispatcher) processSystemCall(payload callMessage, sender frontend.Frontend) (interface{}, error) { + + // Strip prefix + name := strings.TrimPrefix(payload.Name, systemCallPrefix) + + switch name { + case "WindowGetPos": + x, y := sender.WindowGetPos() + return &position{x, y}, nil + case "WindowGetSize": + w, h := sender.WindowGetSize() + return &size{w, h}, nil + default: + return nil, fmt.Errorf("unknown systemcall message: %s", payload.Name) + } + +} diff --git a/v2/internal/frontend/dispatcher/window.go b/v2/internal/frontend/dispatcher/window.go new file mode 100644 index 000000000..204eefbd5 --- /dev/null +++ b/v2/internal/frontend/dispatcher/window.go @@ -0,0 +1,79 @@ +package dispatcher + +import ( + "encoding/json" + "errors" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/pkg/options" + "strconv" + "strings" +) + +func (d *Dispatcher) mustAtoI(input string) int { + result, err := strconv.Atoi(input) + if err != nil { + d.log.Error("cannot convert %s to integer!", input) + } + return result +} + +func (d *Dispatcher) processWindowMessage(message string, sender frontend.Frontend) (string, error) { + if len(message) < 2 { + return "", errors.New("Invalid Event Message: " + message) + } + + switch message[1] { + case 'c': + go sender.WindowCenter() + case 'T': + title := message[2:] + go sender.WindowSetTitle(title) + case 'F': + go sender.WindowFullscreen() + case 'f': + go sender.WindowUnFullscreen() + case 's': + parts := strings.Split(message[3:], ":") + w := d.mustAtoI(parts[0]) + h := d.mustAtoI(parts[1]) + go sender.WindowSetSize(w, h) + case 'p': + parts := strings.Split(message[3:], ":") + x := d.mustAtoI(parts[0]) + y := d.mustAtoI(parts[1]) + go sender.WindowSetPos(x, y) + case 'H': + go sender.WindowHide() + case 'S': + go sender.WindowShow() + case 'r': + var rgba options.RGBA + err := json.Unmarshal([]byte(message[3:]), &rgba) + if err != nil { + return "", err + } + go sender.WindowSetRGBA(&rgba) + case 'M': + go sender.WindowMaximise() + case 'U': + go sender.WindowUnmaximise() + case 'm': + go sender.WindowMinimise() + case 'u': + go sender.WindowUnminimise() + case 'Z': + parts := strings.Split(message[3:], ":") + w := d.mustAtoI(parts[0]) + h := d.mustAtoI(parts[1]) + go sender.WindowSetMaxSize(w, h) + case 'z': + parts := strings.Split(message[3:], ":") + w := d.mustAtoI(parts[0]) + h := d.mustAtoI(parts[1]) + go sender.WindowSetMinSize(w, h) + default: + d.log.Error("unknown Window message: %s", message) + } + + return "", nil +} diff --git a/v2/internal/frontend/events.go b/v2/internal/frontend/events.go new file mode 100644 index 000000000..484e3607a --- /dev/null +++ b/v2/internal/frontend/events.go @@ -0,0 +1,10 @@ +package frontend + +type Events interface { + On(eventName string, callback func(...interface{})) + OnMultiple(eventName string, callback func(...interface{}), counter int) + Once(eventName string, callback func(...interface{})) + Emit(eventName string, data ...interface{}) + Off(eventName string) + Notify(sender Frontend, name string, data ...interface{}) +} diff --git a/v2/internal/frontend/frontend.go b/v2/internal/frontend/frontend.go new file mode 100644 index 000000000..2a4862868 --- /dev/null +++ b/v2/internal/frontend/frontend.go @@ -0,0 +1,105 @@ +package frontend + +import ( + "context" + + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/options" +) + +// FileFilter defines a filter for dialog boxes +type FileFilter struct { + DisplayName string // Filter information EG: "Image Files (*.jpg, *.png)" + Pattern string // semi-colon separated list of extensions, EG: "*.jpg;*.png" +} + +// OpenDialogOptions contains the options for the OpenDialogOptions runtime method +type OpenDialogOptions struct { + DefaultDirectory string + DefaultFilename string + Title string + Filters []FileFilter + AllowFiles bool + AllowDirectories bool + ShowHiddenFiles bool + CanCreateDirectories bool + ResolvesAliases bool + TreatPackagesAsDirectories bool +} + +// SaveDialogOptions contains the options for the SaveDialog runtime method +type SaveDialogOptions struct { + DefaultDirectory string + DefaultFilename string + Title string + Filters []FileFilter + ShowHiddenFiles bool + CanCreateDirectories bool + TreatPackagesAsDirectories bool +} + +type DialogType string + +const ( + InfoDialog DialogType = "info" + WarningDialog DialogType = "warning" + ErrorDialog DialogType = "error" + QuestionDialog DialogType = "question" +) + +// MessageDialogOptions contains the options for the Message dialogs, EG Info, Warning, etc runtime methods +type MessageDialogOptions struct { + Type DialogType + Title string + Message string + Buttons []string + DefaultButton string + CancelButton string + Icon string +} + +type Frontend interface { + Run(context.Context) error + Quit() + + // Dialog + OpenFileDialog(dialogOptions OpenDialogOptions) (string, error) + OpenMultipleFilesDialog(dialogOptions OpenDialogOptions) ([]string, error) + OpenDirectoryDialog(dialogOptions OpenDialogOptions) (string, error) + SaveFileDialog(dialogOptions SaveDialogOptions) (string, error) + MessageDialog(dialogOptions MessageDialogOptions) (string, error) + + // Window + WindowSetTitle(title string) + WindowShow() + WindowHide() + WindowCenter() + WindowMaximise() + WindowUnmaximise() + WindowMinimise() + WindowUnminimise() + WindowSetPos(x int, y int) + WindowGetPos() (int, int) + WindowSetSize(width int, height int) + WindowGetSize() (int, int) + WindowSetMinSize(width int, height int) + WindowSetMaxSize(width int, height int) + WindowFullscreen() + WindowUnFullscreen() + WindowSetRGBA(col *options.RGBA) + WindowReload() + + // Menus + MenuSetApplicationMenu(menu *menu.Menu) + MenuUpdateApplicationMenu() + //SetTrayMenu(menu *menu.TrayMenu) + //UpdateTrayMenuLabel(menu *menu.TrayMenu) + //UpdateContextMenu(contextMenu *menu.ContextMenu) + //DeleteTrayMenuByID(id string) + + // Events + Notify(name string, data ...interface{}) + + // Browser + BrowserOpenURL(url string) +} diff --git a/v2/internal/frontend/runtime/desktop/bindings.js b/v2/internal/frontend/runtime/desktop/bindings.js new file mode 100644 index 000000000..96e1890f6 --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/bindings.js @@ -0,0 +1,67 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +import {Call} from './calls'; + +// This is where we bind go method wrappers +window.go = {}; + +export function SetBindings(bindingsMap) { + try { + bindingsMap = JSON.parse(bindingsMap); + } catch (e) { + console.error(e); + } + + // Initialise the bindings map + window.go = window.go || {}; + + // Iterate package names + Object.keys(bindingsMap).forEach((packageName) => { + + // Create inner map if it doesn't exist + window.go[packageName] = window.go[packageName] || {}; + + // Iterate struct names + Object.keys(bindingsMap[packageName]).forEach((structName) => { + + // Create inner map if it doesn't exist + window.go[packageName][structName] = window.go[packageName][structName] || {}; + + Object.keys(bindingsMap[packageName][structName]).forEach((methodName) => { + + window.go[packageName][structName][methodName] = function () { + + // No timeout by default + let timeout = 0; + + // Actual function + function dynamic() { + const args = [].slice.call(arguments); + return Call([packageName, structName, methodName].join('.'), args, timeout); + } + + // Allow setting timeout to function + dynamic.setTimeout = function (newTimeout) { + timeout = newTimeout; + }; + + // Allow getting timeout to function + dynamic.getTimeout = function () { + return timeout; + }; + + return dynamic; + }(); + }); + }); + }); +} diff --git a/v2/internal/frontend/runtime/desktop/browser.js b/v2/internal/frontend/runtime/desktop/browser.js new file mode 100644 index 000000000..18c5258f2 --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/browser.js @@ -0,0 +1,8 @@ +/** + * @description: Use the system default browser to open the url + * @param {string} url + * @return {void} + */ +export function BrowserOpenURL(url) { + window.WailsInvoke('BO:' + url); +} \ No newline at end of file diff --git a/v2/internal/frontend/runtime/desktop/calls.js b/v2/internal/frontend/runtime/desktop/calls.js new file mode 100644 index 000000000..7b20cadb7 --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/calls.js @@ -0,0 +1,142 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +export const callbacks = {}; + +/** + * Returns a number from the native browser random function + * + * @returns number + */ +function cryptoRandom() { + var array = new Uint32Array(1); + return window.crypto.getRandomValues(array)[0]; +} + +/** + * Returns a number using da old-skool Math.Random + * I likes to call it LOLRandom + * + * @returns number + */ +function basicRandom() { + return Math.random() * 9007199254740991; +} + +// Pick a random number function based on browser capability +var randomFunc; +if (window.crypto) { + randomFunc = cryptoRandom; +} else { + randomFunc = basicRandom; +} + + +/** + * Call sends a message to the backend to call the binding with the + * given data. A promise is returned and will be completed when the + * backend responds. This will be resolved when the call was successful + * or rejected if an error is passed back. + * There is a timeout mechanism. If the call doesn't respond in the given + * time (in milliseconds) then the promise is rejected. + * + * @export + * @param {string} name + * @param {any=} args + * @param {number=} timeout + * @returns + */ +export function Call(name, args, timeout) { + + // Timeout infinite by default + if (timeout == null) { + timeout = 0; + } + + // Create a promise + return new Promise(function (resolve, reject) { + + // Create a unique callbackID + var callbackID; + do { + callbackID = name + '-' + randomFunc(); + } while (callbacks[callbackID]); + + var timeoutHandle; + // Set timeout + if (timeout > 0) { + timeoutHandle = setTimeout(function () { + reject(Error('Call to ' + name + ' timed out. Request ID: ' + callbackID)); + }, timeout); + } + + // Store callback + callbacks[callbackID] = { + timeoutHandle: timeoutHandle, + reject: reject, + resolve: resolve + }; + + try { + const payload = { + name, + args, + callbackID, + }; + + // Make the call + window.WailsInvoke('C' + JSON.stringify(payload)); + } catch (e) { + // eslint-disable-next-line + console.error(e); + } + }); +} + + + +/** + * Called by the backend to return data to a previously called + * binding invocation + * + * @export + * @param {string} incomingMessage + */ +export function Callback(incomingMessage) { + // Decode the message - Credit: https://stackoverflow.com/a/13865680 + //incomingMessage = decodeURIComponent(incomingMessage.replace(/\s+/g, '').replace(/[0-9a-f]{2}/g, '%$&')); + + // Parse the message + var message; + try { + message = JSON.parse(incomingMessage); + } catch (e) { + const error = `Invalid JSON passed to callback: ${e.message}. Message: ${incomingMessage}`; + wails.LogDebug(error); + throw new Error(error); + } + var callbackID = message.callbackid; + var callbackData = callbacks[callbackID]; + if (!callbackData) { + const error = `Callback '${callbackID}' not registered!!!`; + console.error(error); // eslint-disable-line + throw new Error(error); + } + clearTimeout(callbackData.timeoutHandle); + + delete callbacks[callbackID]; + + if (message.error) { + callbackData.reject(message.error); + } else { + callbackData.resolve(message.result); + } +} diff --git a/v2/internal/frontend/runtime/desktop/events.js b/v2/internal/frontend/runtime/desktop/events.js new file mode 100644 index 000000000..464a72c55 --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/events.js @@ -0,0 +1,159 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +// Defines a single listener with a maximum number of times to callback + +/** + * The Listener class defines a listener! :-) + * + * @class Listener + */ +class Listener { + /** + * Creates an instance of Listener. + * @param {function} callback + * @param {number} maxCallbacks + * @memberof Listener + */ + constructor(callback, maxCallbacks) { + // Default of -1 means infinite + maxCallbacks = maxCallbacks || -1; + // Callback invokes the callback with the given data + // Returns true if this listener should be destroyed + this.Callback = (data) => { + callback.apply(null, data); + // If maxCallbacks is infinite, return false (do not destroy) + if (maxCallbacks === -1) { + return false; + } + // Decrement maxCallbacks. Return true if now 0, otherwise false + maxCallbacks -= 1; + return maxCallbacks === 0; + }; + } +} + +export const eventListeners = {}; + +/** + * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + * @param {number} maxCallbacks + */ +export function EventsOnMultiple(eventName, callback, maxCallbacks) { + eventListeners[eventName] = eventListeners[eventName] || []; + const thisListener = new Listener(callback, maxCallbacks); + eventListeners[eventName].push(thisListener); +} + +/** + * Registers an event listener that will be invoked every time the event is emitted + * + * @export + * @param {string} eventName + * @param {function} callback + */ +export function EventsOn(eventName, callback) { + EventsOnMultiple(eventName, callback, -1); +} + +/** + * Registers an event listener that will be invoked once then destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + */ +export function EventsOnce(eventName, callback) { + EventsOnMultiple(eventName, callback, 1); +} + +function notifyListeners(eventData) { + + // Get the event name + let eventName = eventData.name; + + // Check if we have any listeners for this event + if (eventListeners[eventName]) { + + // Keep a list of listener indexes to destroy + const newEventListenerList = eventListeners[eventName].slice(); + + // Iterate listeners + for (let count = 0; count < eventListeners[eventName].length; count += 1) { + + // Get next listener + const listener = eventListeners[eventName][count]; + + let data = eventData.data; + + // Do the callback + const destroy = listener.Callback(data); + if (destroy) { + // if the listener indicated to destroy itself, add it to the destroy list + newEventListenerList.splice(count, 1); + } + } + + // Update callbacks with new list of listeners + eventListeners[eventName] = newEventListenerList; + } +} + +/** + * Notify informs frontend listeners that an event was emitted with the given data + * + * @export + * @param {string} notifyMessage - encoded notification message + + */ +export function EventsNotify(notifyMessage) { + // Parse the message + let message; + try { + message = JSON.parse(notifyMessage); + } catch (e) { + const error = 'Invalid JSON passed to Notify: ' + notifyMessage; + throw new Error(error); + } + notifyListeners(message); +} + +/** + * Emit an event with the given name and data + * + * @export + * @param {string} eventName + */ +export function EventsEmit(eventName) { + + const payload = { + name: eventName, + data: [].slice.apply(arguments).slice(1), + }; + + // Notify JS listeners + notifyListeners(payload); + + // Notify Go listeners + window.WailsInvoke('EE' + JSON.stringify(payload)); +} + +export function EventsOff(eventName) { + // Remove local listeners + eventListeners.delete(eventName); + + // Notify Go listeners + window.WailsInvoke('EX' + eventName); +} \ No newline at end of file diff --git a/v2/internal/frontend/runtime/desktop/ipc.js b/v2/internal/frontend/runtime/desktop/ipc.js new file mode 100644 index 000000000..fee96ac38 --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/ipc.js @@ -0,0 +1,39 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +/** + * WailsInvoke sends the given message to the backend + * + * @param {string} message + */ + +(function () { + // Credit: https://stackoverflow.com/a/2631521 + let _deeptest = function (s) { + var obj = window[s.shift()]; + while (obj && s.length) obj = obj[s.shift()]; + return obj; + }; + let windows = _deeptest(["chrome", "webview", "postMessage"]); + let mac = _deeptest(["webkit", "messageHandlers", "external", "postMessage"]); + + if (!windows && !mac) { + console.error("Unsupported Platform"); + return; + } + + if (windows) { + window.WailsInvoke = (message) => window.chrome.webview.postMessage(message); + } + if (mac) { + window.WailsInvoke = (message) => window.webkit.messageHandlers.external.postMessage(message); + } +})(); \ No newline at end of file diff --git a/v2/internal/frontend/runtime/desktop/log.js b/v2/internal/frontend/runtime/desktop/log.js new file mode 100644 index 000000000..ff52f5919 --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/log.js @@ -0,0 +1,113 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + +/** + * Sends a log message to the backend with the given level + message + * + * @param {string} level + * @param {string} message + */ +function sendLogMessage(level, message) { + + // Log Message format: + // l[type][message] + window.WailsInvoke('L' + level + message); +} + +/** + * Log the given trace message with the backend + * + * @export + * @param {string} message + */ +export function LogTrace(message) { + sendLogMessage('T', message); +} + +/** + * Log the given message with the backend + * + * @export + * @param {string} message + */ +export function LogPrint(message) { + sendLogMessage('P', message); +} + +/** + * Log the given debug message with the backend + * + * @export + * @param {string} message + */ +export function LogDebug(message) { + sendLogMessage('D', message); +} + +/** + * Log the given info message with the backend + * + * @export + * @param {string} message + */ +export function LogInfo(message) { + sendLogMessage('I', message); +} + +/** + * Log the given warning message with the backend + * + * @export + * @param {string} message + */ +export function LogWarning(message) { + sendLogMessage('W', message); +} + +/** + * Log the given error message with the backend + * + * @export + * @param {string} message + */ +export function LogError(message) { + sendLogMessage('E', message); +} + +/** + * Log the given fatal message with the backend + * + * @export + * @param {string} message + */ +export function LogFatal(message) { + sendLogMessage('F', message); +} + +/** + * Sets the Log level to the given log level + * + * @export + * @param {number} loglevel + */ +export function SetLogLevel(loglevel) { + sendLogMessage('S', loglevel); +} + +// Log levels +export const LogLevel = { + TRACE: 1, + DEBUG: 2, + INFO: 3, + WARNING: 4, + ERROR: 5, +}; diff --git a/v2/internal/frontend/runtime/desktop/main.js b/v2/internal/frontend/runtime/desktop/main.js new file mode 100644 index 000000000..5e0b6feb5 --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/main.js @@ -0,0 +1,87 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 9 */ +import * as Log from './log'; +import {eventListeners, EventsEmit, EventsNotify, EventsOff, EventsOn, EventsOnce, EventsOnMultiple} from './events'; +import {Callback, callbacks} from './calls'; +import {SetBindings} from "./bindings"; +import * as Window from "./window"; +import * as Browser from "./browser"; + + +export function Quit() { + window.WailsInvoke('Q'); +} + +// The JS runtime +window.runtime = { + ...Log, + ...Window, + ...Browser, + EventsOn, + EventsOnce, + EventsOnMultiple, + EventsEmit, + EventsOff, + Quit +}; + +// Internal wails endpoints +window.wails = { + Callback, + EventsNotify, + SetBindings, + eventListeners, + callbacks, + flags: { + disableScrollbarDrag: false, + disableWailsDefaultContextMenu: false, + } +}; + +// Set the bindings +window.wails.SetBindings(window.wailsbindings); +delete window.wails.SetBindings; + +// This is evaluated at build time in package.json +// const dev = 0; +// const production = 1; +if (ENV === 0) { + delete window.wailsbindings; +} + +// Setup drag handler +// Based on code from: https://github.com/patr0nus/DeskGap +window.addEventListener('mousedown', (e) => { + let currentElement = e.target; + while (currentElement != null) { + if (currentElement.hasAttribute('data-wails-no-drag')) { + break; + } else if (currentElement.hasAttribute('data-wails-drag')) { + if (window.wails.flags.disableScrollbarDrag) { + // This checks for clicks on the scroll bar + if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) { + break; + } + } + window.WailsInvoke("drag"); + e.preventDefault(); + break; + } + currentElement = currentElement.parentElement; + } +}); + +// Setup context menu hook +window.addEventListener('contextmenu', function (e) { + if (window.wails.flags.disableWailsDefaultContextMenu) { + e.preventDefault(); + } +}); \ No newline at end of file diff --git a/v2/internal/frontend/runtime/desktop/window.js b/v2/internal/frontend/runtime/desktop/window.js new file mode 100644 index 000000000..448dc5e3d --- /dev/null +++ b/v2/internal/frontend/runtime/desktop/window.js @@ -0,0 +1,187 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 9 */ + + +import {Call} from "./calls"; + +export function WindowReload() { + window.location.reload(); +} + +/** + * Place the window in the center of the screen + * + * @export + */ +export function WindowCenter() { + window.WailsInvoke('Wc'); +} + +/** + * Sets the window title + * + * @param {string} title + * @export + */ +export function WindowSetTitle(title) { + window.WailsInvoke('WT' + title); +} + +/** + * Makes the window go fullscreen + * + * @export + */ +export function WindowFullscreen() { + window.WailsInvoke('WF'); +} + +/** + * Reverts the window from fullscreen + * + * @export + */ +export function WindowUnFullscreen() { + window.WailsInvoke('Wf'); +} + +/** + * Set the Size of the window + * + * @export + * @param {number} width + * @param {number} height + */ +export function WindowSetSize(width, height) { + window.WailsInvoke('Ws:' + width + ':' + height); +} + +/** + * Get the Size of the window + * + * @export + * @return {Promise<{w: number, h: number}>} The size of the window + + */ +export function WindowGetSize() { + return Call(":wails:WindowGetSize"); +} + +/** + * Set the maximum size of the window + * + * @export + * @param {number} width + * @param {number} height + */ +export function WindowSetMaxSize(width, height) { + window.WailsInvoke('WZ:' + width + ':' + height); +} + +/** + * Set the minimum size of the window + * + * @export + * @param {number} width + * @param {number} height + */ +export function WindowSetMinSize(width, height) { + window.WailsInvoke('Wz:' + width + ':' + height); +} + +/** + * Set the Position of the window + * + * @export + * @param {number} x + * @param {number} y + */ +export function WindowSetPosition(x, y) { + window.WailsInvoke('Wp:' + x + ':' + y); +} + +/** + * Get the Position of the window + * + * @export + * @return {Promise<{x: number, y: number}>} The position of the window + */ +export function WindowGetPosition() { + return Call(":wails:WindowGetPos"); +} + +/** + * Hide the Window + * + * @export + */ +export function WindowHide() { + window.WailsInvoke('WH'); +} + +/** + * Show the Window + * + * @export + */ +export function WindowShow() { + window.WailsInvoke('WS'); +} + +/** + * Maximise the Window + * + * @export + */ +export function WindowMaximise() { + window.WailsInvoke('WM'); +} + +/** + * Unmaximise the Window + * + * @export + */ +export function WindowUnmaximise() { + window.WailsInvoke('WU'); +} + +/** + * Minimise the Window + * + * @export + */ +export function WindowMinimise() { + window.WailsInvoke('Wm'); +} + +/** + * Unminimise the Window + * + * @export + */ +export function WindowUnminimise() { + window.WailsInvoke('Wu'); +} + + +/** + * Sets the background colour of the window + * + * @export + * @param {RGBA} RGBA background colour + */ +export function WindowSetRGBA(RGBA) { + let rgba = JSON.stringify(RGBA); + window.WailsInvoke('Wr:' + rgba); +} + diff --git a/v2/internal/frontend/runtime/dev/Overlay.svelte b/v2/internal/frontend/runtime/dev/Overlay.svelte new file mode 100644 index 000000000..dfe02b21b --- /dev/null +++ b/v2/internal/frontend/runtime/dev/Overlay.svelte @@ -0,0 +1,54 @@ + + +{#if $overlayVisible } +
+
+
+
+
+{/if} + + \ No newline at end of file diff --git a/v2/internal/frontend/runtime/dev/build.js b/v2/internal/frontend/runtime/dev/build.js new file mode 100644 index 000000000..6e5e8bb2f --- /dev/null +++ b/v2/internal/frontend/runtime/dev/build.js @@ -0,0 +1,15 @@ +/* jshint esversion: 8 */ +const esbuild = require("esbuild"); +const sveltePlugin = require("esbuild-svelte"); + +esbuild + .build({ + entryPoints: ["main.js"], + bundle: true, + minify: true, + outfile: "../ipc_websocket.js", + plugins: [sveltePlugin({compileOptions: {css: true}})], + logLevel: "info", + sourcemap: "inline", + }) + .catch(() => process.exit(1)); \ No newline at end of file diff --git a/v2/internal/frontend/runtime/dev/log.js b/v2/internal/frontend/runtime/dev/log.js new file mode 100644 index 000000000..e128c97f0 --- /dev/null +++ b/v2/internal/frontend/runtime/dev/log.js @@ -0,0 +1,8 @@ +export function log(message) { + // eslint-disable-next-line + console.log( + '%c wails dev %c ' + message + ' ', + 'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem', + 'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem' + ); +} \ No newline at end of file diff --git a/v2/internal/frontend/runtime/dev/main.js b/v2/internal/frontend/runtime/dev/main.js new file mode 100644 index 000000000..dac1a8782 --- /dev/null +++ b/v2/internal/frontend/runtime/dev/main.js @@ -0,0 +1,119 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +import {log} from "./log"; +import Overlay from "./Overlay.svelte"; +import {hideOverlay, showOverlay} from "./store"; + +let components = {}; +window.ipcCallbacks = []; +window.ipcCallbackNames = []; + +window.awaitIPC = (name, callback) => { + if (!window.ipcCallbacks) return callback; + log("Queuing '" + name + "' for execution once ipc ready."); + window.ipcCallbackNames.push(name); + window.ipcCallbacks.push(callback); +}; + +window.addEventListener('DOMContentLoaded', () => { + components.overlay = new Overlay({ + target: document.body, + anchor: document.querySelector('#wails-spinner'), + }); +}); + +let websocket = null; +let connectTimer; + +window.onbeforeunload = function () { + if (websocket) { + websocket.onclose = function () { + }; + websocket.close(); + websocket = null; + } +}; + +// ...and attempt to connect +connect(); + +function setupIPCBridge() { + window.WailsInvoke = (message) => { + websocket.send(message); + }; + for (let i = 0; i < window.ipcCallbacks.length; i++) { + log("Executing JS: " + window.ipcCallbackNames[i]); + window.ipcCallbacks[i](); + } + delete window.ipcCallbacks; + delete window.ipcCallbackNames; +} + +// Handles incoming websocket connections +function handleConnect() { + log('Connected to backend'); + hideOverlay(); + setupIPCBridge(); + clearInterval(connectTimer); + websocket.onclose = handleDisconnect; + websocket.onmessage = handleMessage; +} + +// Handles websocket disconnects +function handleDisconnect() { + log('Disconnected from backend'); + websocket = null; + showOverlay(); + connect(); +} + +function _connect() { + if (websocket == null) { + websocket = new WebSocket('ws://' + window.location.hostname + ':34115/wails/ipc'); + websocket.onopen = handleConnect; + websocket.onerror = function (e) { + e.stopImmediatePropagation(); + e.stopPropagation(); + e.preventDefault(); + websocket = null; + return false; + }; + } +} + +// Try to connect to the backend every .5s +function connect() { + _connect(); + connectTimer = setInterval(_connect, 500); +} + +function handleMessage(message) { + + if (message.data === "reload") { + window.runtime.WindowReload(); + return; + } + + // As a bridge we ignore js and css injections + switch (message.data[0]) { + // Notifications + case 'n': + window.wails.EventsNotify(message.data.slice(1)); + break; + case 'c': + const callbackData = message.data.slice(1); + window.wails.Callback(callbackData); + break; + default: + log('Unknown message: ' + message.data); + } +} diff --git a/v2/internal/frontend/runtime/dev/package-lock.json b/v2/internal/frontend/runtime/dev/package-lock.json new file mode 100644 index 000000000..4413e7f6b --- /dev/null +++ b/v2/internal/frontend/runtime/dev/package-lock.json @@ -0,0 +1,667 @@ +{ + "name": "dev", + "version": "2.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", + "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "esbuild": { + "version": "0.12.21", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.21.tgz", + "integrity": "sha512-7hyXbU3g94aREufI/5nls7Xcc+RGQeZWZApm6hoBaFvt2BPtpT4TjFMQ9Tb1jU8XyBGz00ShmiyflCogphMHFQ==", + "dev": true + }, + "esbuild-svelte": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/esbuild-svelte/-/esbuild-svelte-0.5.6.tgz", + "integrity": "sha512-Bz8nU45FrT6sP/Tf3M2rQUuBGxnDSNSPZNIoYwSNt5H+wjSyo/t+zm94tgnOZsR6GgpDMbNQgo4jGbK0NLvEfw==", + "dev": true, + "requires": { + "svelte": "^3.42.6" + }, + "dependencies": { + "svelte": { + "version": "3.43.1", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.43.1.tgz", + "integrity": "sha512-nvPIaKx4HLzYlSdquISZpgG1Kqr2VAWQjZOt3Iwm3UhbqmA0LnSx4k1YpRMEhjQYW3ZCqQoK8Egto9tv4YewMA==", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz", + "integrity": "sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==", + "dev": true + }, + "string.prototype.padend": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz", + "integrity": "sha512-/AQFLdYvePENU3W5rgurfWSMU6n+Ww8n/3cUt7E+vPBB/D7YDG8x+qjoFs4M/alR2bW7Qg6xMjVwWUOvuQ0XpQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svelte": { + "version": "3.42.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.42.2.tgz", + "integrity": "sha512-FOyNYKXb8wdE0Ot+Ctt2/OyDLsNBP8+V6PUE9ag6ZKeLslIou0LnMu1fhtWUA+HjzKTbAM1yj+4PFLtg/3pMJA==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + } + } +} diff --git a/v2/internal/frontend/runtime/dev/package.json b/v2/internal/frontend/runtime/dev/package.json new file mode 100644 index 000000000..a8fdade52 --- /dev/null +++ b/v2/internal/frontend/runtime/dev/package.json @@ -0,0 +1,18 @@ +{ + "name": "dev", + "version": "2.0.0", + "description": "Wails JS Dev", + "main": "main.js", + "scripts": { + "build": "run-p build:*", + "build:dev": "node build.js" + }, + "author": "Lea Anthony ", + "license": "ISC", + "devDependencies": { + "esbuild": "^0.12.17", + "npm-run-all": "^4.1.5", + "svelte": "^3.42.2", + "esbuild-svelte": "^0.5.6" + } +} diff --git a/v2/internal/frontend/runtime/dev/store.js b/v2/internal/frontend/runtime/dev/store.js new file mode 100644 index 000000000..fc085570b --- /dev/null +++ b/v2/internal/frontend/runtime/dev/store.js @@ -0,0 +1,12 @@ +import {writable} from 'svelte/store'; + +/** Overlay */ +export const overlayVisible = writable(false); + +export function showOverlay() { + overlayVisible.set(true); +} + +export function hideOverlay() { + overlayVisible.set(false); +} diff --git a/v2/internal/frontend/runtime/events.go b/v2/internal/frontend/runtime/events.go new file mode 100644 index 000000000..d5a63855f --- /dev/null +++ b/v2/internal/frontend/runtime/events.go @@ -0,0 +1,150 @@ +package runtime + +import ( + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/logger" + "sync" +) + +// eventListener holds a callback function which is invoked when +// the event listened for is emitted. It has a counter which indicates +// how the total number of events it is interested in. A value of zero +// means it does not expire (default). +type eventListener struct { + callback func(...interface{}) // Function to call with emitted event data + counter int // The number of times this callback may be called. -1 = infinite + delete bool // Flag to indicate that this listener should be deleted +} + +// Events handles eventing +type Events struct { + log *logger.Logger + frontend []frontend.Frontend + + // Go event listeners + listeners map[string][]*eventListener + notifyLock sync.RWMutex +} + +func (e *Events) Notify(sender frontend.Frontend, name string, data ...interface{}) { + e.notifyBackend(name, data...) + for _, thisFrontend := range e.frontend { + if thisFrontend == sender { + continue + } + thisFrontend.Notify(name, data...) + } +} + +func (e *Events) On(eventName string, callback func(...interface{})) { + e.registerListener(eventName, callback, -1) +} + +func (e *Events) OnMultiple(eventName string, callback func(...interface{}), counter int) { + e.registerListener(eventName, callback, counter) +} + +func (e *Events) Once(eventName string, callback func(...interface{})) { + e.registerListener(eventName, callback, 1) +} + +func (e *Events) Emit(eventName string, data ...interface{}) { + e.notifyBackend(eventName, data...) + for _, thisFrontend := range e.frontend { + thisFrontend.Notify(eventName, data...) + } +} + +func (e *Events) Off(eventName string) { + e.unRegisterListener(eventName) +} + +// NewEvents creates a new log subsystem +func NewEvents(log *logger.Logger) *Events { + result := &Events{ + log: log, + listeners: make(map[string][]*eventListener), + } + return result +} + +// registerListener provides a means of subscribing to events of type "eventName" +func (e *Events) registerListener(eventName string, callback func(...interface{}), counter int) { + // Create new eventListener + thisListener := &eventListener{ + callback: callback, + counter: counter, + delete: false, + } + e.notifyLock.Lock() + // Append the new listener to the listeners slice + e.listeners[eventName] = append(e.listeners[eventName], thisListener) + e.notifyLock.Unlock() +} + +// unRegisterListener provides a means of unsubscribing to events of type "eventName" +func (e *Events) unRegisterListener(eventName string) { + e.notifyLock.Lock() + // Clear the listeners + delete(e.listeners, eventName) + e.notifyLock.Unlock() +} + +// Notify backend for the given event name +func (e *Events) notifyBackend(eventName string, data ...interface{}) { + + // Get list of event listeners + listeners := e.listeners[eventName] + if listeners == nil { + e.log.Trace("No listeners for event '%s'", eventName) + return + } + + // Lock the listeners + e.notifyLock.Lock() + + // We have a dirty flag to indicate that there are items to delete + itemsToDelete := false + + // Callback in goroutine + for _, listener := range listeners { + if listener.counter > 0 { + listener.counter-- + } + go listener.callback(data...) + + if listener.counter == 0 { + listener.delete = true + itemsToDelete = true + } + } + + // Do we have items to delete? + if itemsToDelete == true { + + // Create a new Listeners slice + var newListeners []*eventListener + + // Iterate over current listeners + for _, listener := range listeners { + // If we aren't deleting the listener, add it to the new list + if !listener.delete { + newListeners = append(newListeners, listener) + } + } + + // Save new listeners or remove entry + if len(newListeners) > 0 { + e.listeners[eventName] = newListeners + } else { + delete(e.listeners, eventName) + } + } + + // Unlock + e.notifyLock.Unlock() +} + +func (e *Events) AddFrontend(appFrontend frontend.Frontend) { + e.frontend = append(e.frontend, appFrontend) +} diff --git a/v2/internal/frontend/runtime/ipc.go b/v2/internal/frontend/runtime/ipc.go new file mode 100644 index 000000000..1f8873796 --- /dev/null +++ b/v2/internal/frontend/runtime/ipc.go @@ -0,0 +1,8 @@ +//go:build darwin || windows + +package runtime + +import _ "embed" + +//go:embed ipc.js +var DesktopIPC []byte diff --git a/v2/internal/frontend/runtime/ipc.js b/v2/internal/frontend/runtime/ipc.js new file mode 100644 index 000000000..6947faa84 --- /dev/null +++ b/v2/internal/frontend/runtime/ipc.js @@ -0,0 +1 @@ +(()=>{(function(){let o=function(e){for(var s=window[e.shift()];s&&e.length;)s=s[e.shift()];return s},t=o(["chrome","webview","postMessage"]),n=o(["webkit","messageHandlers","external","postMessage"]);if(!t&&!n){console.error("Unsupported Platform");return}t&&(window.WailsInvoke=e=>window.chrome.webview.postMessage(e)),n&&(window.WailsInvoke=e=>window.webkit.messageHandlers.external.postMessage(e))})();})(); diff --git a/v2/internal/frontend/runtime/ipc_websocket.go b/v2/internal/frontend/runtime/ipc_websocket.go new file mode 100644 index 000000000..e0b5ca1f2 --- /dev/null +++ b/v2/internal/frontend/runtime/ipc_websocket.go @@ -0,0 +1,8 @@ +//go:build dev + +package runtime + +import _ "embed" + +//go:embed ipc_websocket.js +var WebsocketIPC []byte diff --git a/v2/internal/frontend/runtime/ipc_websocket.js b/v2/internal/frontend/runtime/ipc_websocket.js new file mode 100644 index 000000000..16774652b --- /dev/null +++ b/v2/internal/frontend/runtime/ipc_websocket.js @@ -0,0 +1,22 @@ +(()=>{function F(t){console.log("%c wails dev %c "+t+" ","background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem","background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem")}function _(){}var j=t=>t;function G(t){return t()}function tt(){return Object.create(null)}function b(t){t.forEach(G)}function S(t){return typeof t=="function"}function O(t,e){return t!=t?e==e:t!==e||t&&typeof t=="object"||typeof t=="function"}function et(t){return Object.keys(t).length===0}function nt(t,...e){if(t==null)return _;let n=t.subscribe(...e);return n.unsubscribe?()=>n.unsubscribe():n}function it(t,e,n){t.$$.on_destroy.push(nt(e,n))}var ot=typeof window!="undefined",Et=ot?()=>window.performance.now():()=>Date.now(),q=ot?t=>requestAnimationFrame(t):_;var x=new Set;function rt(t){x.forEach(e=>{e.c(t)||(x.delete(e),e.f())}),x.size!==0&&q(rt)}function jt(t){let e;return x.size===0&&q(rt),{promise:new Promise(n=>{x.add(e={c:t,f:n})}),abort(){x.delete(e)}}}var st=!1;function Ot(){st=!0}function Dt(){st=!1}function At(t,e){t.appendChild(e)}function ct(t,e,n){let i=K(t);if(!i.getElementById(e)){let o=A("style");o.id=e,o.textContent=n,lt(i,o)}}function K(t){if(!t)return document;let e=t.getRootNode?t.getRootNode():t.ownerDocument;return e.host?e:document}function Lt(t){let e=A("style");return lt(K(t),e),e}function lt(t,e){At(t.head||t,e)}function P(t,e,n){t.insertBefore(e,n||null)}function D(t){t.parentNode.removeChild(t)}function A(t){return document.createElement(t)}function Bt(t){return document.createTextNode(t)}function ut(){return Bt("")}function at(t,e,n){n==null?t.removeAttribute(e):t.getAttribute(e)!==n&&t.setAttribute(e,n)}function Jt(t){return Array.from(t.childNodes)}function zt(t,e,n=!1){let i=document.createEvent("CustomEvent");return i.initCustomEvent(t,n,!1,e),i}var N=new Set,L=0;function Tt(t){let e=5381,n=t.length;for(;n--;)e=(e<<5)-e^t.charCodeAt(n);return e>>>0}function ft(t,e,n,i,o,c,s,l=0){let a=16.666/i,r=`{ +`;for(let g=0;g<=1;g+=a){let v=e+(n-e)*c(g);r+=g*100+`%{${s(v,1-v)}} +`}let y=r+`100% {${s(n,1-n)}} +}`,f=`__svelte_${Tt(y)}_${l}`,u=K(t);N.add(u);let h=u.__svelte_stylesheet||(u.__svelte_stylesheet=Lt(t).sheet),p=u.__svelte_rules||(u.__svelte_rules={});p[f]||(p[f]=!0,h.insertRule(`@keyframes ${f} ${y}`,h.cssRules.length));let w=t.style.animation||"";return t.style.animation=`${w?`${w}, `:""}${f} ${i}ms linear ${o}ms 1 both`,L+=1,f}function Ht(t,e){let n=(t.style.animation||"").split(", "),i=n.filter(e?c=>c.indexOf(e)<0:c=>c.indexOf("__svelte")===-1),o=n.length-i.length;o&&(t.style.animation=i.join(", "),L-=o,L||Gt())}function Gt(){q(()=>{L||(N.forEach(t=>{let e=t.__svelte_stylesheet,n=e.cssRules.length;for(;n--;)e.deleteRule(n);t.__svelte_rules={}}),N.clear())})}var dt;function B(t){dt=t}var k=[];var ht=[],J=[],pt=[],qt=Promise.resolve(),R=!1;function Kt(){R||(R=!0,qt.then(_t))}function M(t){J.push(t)}var W=!1,V=new Set;function _t(){if(!W){W=!0;do{for(let t=0;t{$=null})),$}function U(t,e,n){t.dispatchEvent(zt(`${e?"intro":"outro"}${n}`))}var z=new Set,m;function mt(){m={r:0,c:[],p:m}}function yt(){m.r||b(m.c),m=m.p}function I(t,e){t&&t.i&&(z.delete(t),t.i(e))}function X(t,e,n,i){if(t&&t.o){if(z.has(t))return;z.add(t),m.c.push(()=>{z.delete(t),i&&(n&&t.d(1),i())}),t.o(e)}}var Rt={duration:0};function Z(t,e,n,i){let o=e(t,n),c=i?0:1,s=null,l=null,a=null;function r(){a&&Ht(t,a)}function y(u,h){let p=u.b-c;return h*=Math.abs(p),{a:c,b:u.b,d:p,duration:h,start:u.start,end:u.start+h,group:u.group}}function f(u){let{delay:h=0,duration:p=300,easing:w=j,tick:g=_,css:v}=o||Rt,H={start:Et()+h,b:u};u||(H.group=m,m.r+=1),s||l?l=H:(v&&(r(),a=ft(t,c,u,p,h,w,v)),u&&g(0,1),s=y(H,p),M(()=>U(t,u,"start")),jt(E=>{if(l&&E>l.start&&(s=y(l,p),l=null,U(t,s.b,"start"),v&&(r(),a=ft(t,c,s.b,s.duration,0,w,o.css))),s){if(E>=s.end)g(c=s.b,1-c),U(t,s.b,"end"),l||(s.b?r():--s.group.r||b(s.group.c)),s=null;else if(E>=s.start){let It=E-s.start;c=s.a+s.d*w(It/s.duration),g(c,1-c)}}return!!(s||l)}))}return{run(u){S(o)?Nt().then(()=>{o=o(),f(u)}):f(u)},end(){r(),s=l=null}}}var re=typeof window!="undefined"?window:typeof globalThis!="undefined"?globalThis:global;var se=new Set(["allowfullscreen","allowpaymentrequest","async","autofocus","autoplay","checked","controls","default","defer","disabled","formnovalidate","hidden","ismap","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","selected"]);function Wt(t,e,n,i){let{fragment:o,on_mount:c,on_destroy:s,after_update:l}=t.$$;o&&o.m(e,n),i||M(()=>{let a=c.map(G).filter(S);s?s.push(...a):b(a),t.$$.on_mount=[]}),l.forEach(M)}function gt(t,e){let n=t.$$;n.fragment!==null&&(b(n.on_destroy),n.fragment&&n.fragment.d(e),n.on_destroy=n.fragment=null,n.ctx=[])}function Vt(t,e){t.$$.dirty[0]===-1&&(k.push(t),Kt(),t.$$.dirty.fill(0)),t.$$.dirty[e/31|0]|=1<{let p=h.length?h[0]:u;return r.ctx&&o(r.ctx[f],r.ctx[f]=p)&&(!r.skip_bound&&r.bound[f]&&r.bound[f](p),y&&Vt(t,f)),u}):[],r.update(),y=!0,b(r.before_update),r.fragment=i?i(r.ctx):!1,e.target){if(e.hydrate){Ot();let f=Jt(e.target);r.fragment&&r.fragment.l(f),f.forEach(D)}else r.fragment&&r.fragment.c();e.intro&&I(t.$$.fragment),Wt(t,e.target,e.anchor,e.customElement),Dt(),_t()}B(a)}var Ut;typeof HTMLElement=="function"&&(Ut=class extends HTMLElement{constructor(){super();this.attachShadow({mode:"open"})}connectedCallback(){let{on_mount:t}=this.$$;this.$$.on_disconnect=t.map(G).filter(S);for(let e in this.$$.slotted)this.appendChild(this.$$.slotted[e])}attributeChangedCallback(t,e,n){this[t]=n}disconnectedCallback(){b(this.$$.on_disconnect)}$destroy(){gt(this,1),this.$destroy=_}$on(t,e){let n=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return n.push(e),()=>{let i=n.indexOf(e);i!==-1&&n.splice(i,1)}}$set(t){this.$$set&&!et(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}});var Q=class{$destroy(){gt(this,1),this.$destroy=_}$on(e,n){let i=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return i.push(n),()=>{let o=i.indexOf(n);o!==-1&&i.splice(o,1)}}$set(e){this.$$set&&!et(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}};var C=[];function wt(t,e=_){let n,i=new Set;function o(l){if(O(t,l)&&(t=l,n)){let a=!C.length;for(let r of i)r[1](),C.push(r,t);if(a){for(let r=0;r{i.delete(r),i.size===0&&(n(),n=null)}}return{set:o,update:c,subscribe:s}}var T=wt(!1);function vt(){T.set(!0)}function Ft(){T.set(!1)}function Y(t,{delay:e=0,duration:n=400,easing:i=j}={}){let o=+getComputedStyle(t).opacity;return{delay:e,duration:n,easing:i,css:c=>`opacity: ${c*o}`}}function Xt(t){ct(t,"svelte-181h7z",`.wails-reconnect-overlay.svelte-181h7z{position:fixed;top:0;left:0;width:100%;height:100%;backdrop-filter:blur(2px) saturate(0%) contrast(50%) brightness(25%);z-index:999999 + }.wails-reconnect-overlay-content.svelte-181h7z{position:relative;top:50%;transform:translateY(-50%);margin:0;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAAA7CAMAAAAEsocZAAAC91BMVEUAAACzQ0PjMjLkMjLZLS7XLS+vJCjkMjKlEx6uGyHjMDGiFx7GJyrAISjUKy3mMzPlMjLjMzOsGyDKJirkMjK6HyXmMjLgMDC6IiLcMjLULC3MJyrRKSy+IibmMzPmMjK7ISXlMjLIJimzHSLkMjKtGiHZLC7BIifgMDCpGSDFIivcLy+yHSKoGR+eFBzNKCvlMjKxHSPkMTKxHSLmMjLKJyq5ICXDJCe6ISXdLzDkMjLmMzPFJSm2HyTlMTLhMDGyHSKUEBmhFx24HyTCJCjHJijjMzOiFh7mMjJ6BhDaLDCuGyOKABjnMzPGJinJJiquHCGEChSmGB/pMzOiFh7VKy3OKCu1HiSvHCLjMTLMKCrBIyeICxWxHCLDIyjSKizBIyh+CBO9ISa6ISWDChS9Iie1HyXVLC7FJSrLKCrlMjLiMTGPDhicFRywGyKXFBuhFx1/BxO7IiXkMTGeFBx8BxLkMTGnGR/GJCi4ICWsGyGJDxXSLS2yGiHSKi3CJCfnMzPQKiyECRTKJiq6ISWUERq/Iye0HiPDJCjGJSm6ICaPDxiTEBrdLy+3HyXSKiy0HyOQEBi4ICWhFh1+CBO9IieODhfSKyzWLC2LDhh8BxHKKCq7ISWaFBzkMzPqNDTTLC3EJSiHDBacExyvGyO1HyTPKCy+IieoGSC7ISaVEhrMKCvQKyusGyG0HiKACBPIJSq/JCaABxR5BRLEJCnkMzPJJinEJimPDRZ2BRKqHx/jMjLnMzPgMDHULC3NKSvQKSzsNDTWLS7SKyy3HyTKJyrDJSjbLzDYLC6mGB/GJSnVLC61HiPLKCrHJSm/Iye8Iia6ICWzHSKxHCLaLi/PKSupGR+7ICXpMzPbLi/IJinJJSmsGyGrGiCkFx6PDheJCxaFChXBIyfAIieSDxmBCBPlMjLeLzDdLzC5HySMDRe+ISWvGyGcFBzSKSzPJyvMJyrEJCjDIyefFRyWERriMDHUKiy/ISaZExv0NjbwNTXuNDTrMzMI0c+yAAAAu3RSTlMAA8HR/gwGgAj+MEpGCsC+hGpjQjYnIxgWBfzx7urizMrFqqB1bF83KhsR/fz8+/r5+fXv7unZ1tC+t6mmopqKdW1nYVpVRjUeHhIQBPr59/b28/Hx8ODg3NvUw8O/vKeim5aNioiDgn1vZWNjX1xUU1JPTUVFPT08Mi4qJyIh/Pv7+/n4+Pf39fT08/Du7efn5uXj4uHa19XNwsG/vrq2tbSuramlnpyYkpGNiIZ+enRraGVjVVBKOzghdjzRsAAABJVJREFUWMPtllVQG1EYhTc0ASpoobS0FCulUHd3oUjd3d3d3d3d3d2b7CYhnkBCCHGDEIK7Vh56d0NpOgwkYfLQzvA9ZrLfnPvfc+8uVEst/yheBJup3Nya2MjU6pa/jWLZtxjXpZFtVB4uVNI6m5gIruNkVFebqIb5Ug2ym4TIEM/gtUOGbg613oBzjAzZFrZ+lXu/3TIiMXXS5M6HTvrNHeLpZLEh6suGNW9fzZ9zd/qVi2eOHygqi5cDE5GUrJocONgzyqo0UXNSUlKSEhMztFqtXq9vNxImAmS3g7Y6QlbjdBWVGW36jt4wDGTUXjUsafh5zJWRkdFuZGtWGnCRmg+HasiGMUClTTzW0ZuVgLlGDIPM4Lhi0IrVq+tv2hS21fNrSONQgpM9DsJ4t3fM9PkvJuKj2ZjrZwvILKvaSTgciUSirjt6dOfOpyd169bDb9rMOwF9Hj4OD100gY0YXYb299bjzMrqj9doNByJWlVXFB9DT5dmJuvy+cq83JyuS6ayEYSHulKL8dmFnBkrCeZlHKMrC5XRhXGCZB2Ty1fkleRQaMCFT2DBsEafzRFJu7/2MicbKynPhQUDLiZwMWLJZKNLzoLbJBYVcurSmbmn+rcyJ8vCMgmlmaW6gnwun/+3C96VpAUuET1ZgRR36r2xWlnYSnf3oKABA14uXDDvydxHs6cpTV1p3hlJ2rJCiUjIZCByItXg8sHJijuvT64CuMTABUYvb6NN1Jdp1PH7D7f3bo2eS5KvW4RJr7atWT5w4MBBg9zdBw9+37BS7QIoFS5WnIaj12dr1DEXFgdvr4fh4eFl+u/wz8uf3jjHic8s4DL2Dal0IANyUBeCRCcwOBJV26JsjSpGwHVuSai69jvqD+jr56OgtKy0zAAK5mLTVBKVKL5tNthGAR9JneJQ/bFsHNzy+U7IlCYROxtMpIjR0ceoQVnowracLLpAQWETqV361bPoFo3cEbz2zYLZM7t3HWXcxmiBOgttS1ycWkTXMWh4mGigdug9DFdttqCFgTN6nD0q1XEVSoCxEjyFCi2eNC6Z69MRVIImJ6JQSf5gcFVCuF+aDhCa1F6MJFDaiNBQAh2TMfWBjhmLsAxUjG/fmjs0qjJck8D0GPBcuUuZW1LS/tIsPzqmQt17PvZQknlwnf4tHDBc+7t5VV3QQCkdc+Ur8/hdrz0but0RCumWiYbiKmLJ7EVbRomj4Q7+y5wsaXvfTGFpQcHB7n2WbG4MGdniw2Tm8xl5Yhr7MrSYHQ3uampz10aWyHyuzxvqaW/6W4MjXAUD3QV2aw97ZxhGjxCohYf5TpTHMXU1BbsAuoFnkRygVieIGAbqiF7rrH4rfWpKJouBCtyHJF8ctEyGubBa+C6NsMYEUonJFITHZqWBxXUA12Dv76Tf/PgOBmeNiiLG1pcKo1HAq8jLpY4JU1yWEixVNaOgoRJAKBSZHTZTU+wJOMtUDZvlVITC6FTlksyrEBoPHXpxxbzdaqzigUtVDkJVIOtVQ9UEOR4VGUh/kHWq0edJ6CxnZ+eePXva2bnY/cF/I1RLLf8vvwDANdMSMegxcAAAAABJRU5ErkJggg==);background-repeat:no-repeat;background-position:center + }.wails-reconnect-overlay-loadingspinner.svelte-181h7z{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#f00 #eee0 #f00 #eee0;border-radius:50%;animation:svelte-181h7z-loadingspin 1s linear infinite;margin:auto;padding:2.5em + }@keyframes svelte-181h7z-loadingspin{100%{transform:rotate(360deg)}}`)}function xt(t){let e,n,i;return{c(){e=A("div"),e.innerHTML='
',at(e,"class","wails-reconnect-overlay svelte-181h7z")},m(o,c){P(o,e,c),i=!0},i(o){i||(M(()=>{n||(n=Z(e,Y,{duration:300},!0)),n.run(1)}),i=!0)},o(o){n||(n=Z(e,Y,{duration:300},!1)),n.run(0),i=!1},d(o){o&&D(e),o&&n&&n.end()}}}function Zt(t){let e,n,i=t[0]&&xt(t);return{c(){i&&i.c(),e=ut()},m(o,c){i&&i.m(o,c),P(o,e,c),n=!0},p(o,[c]){o[0]?i?c&1&&I(i,1):(i=xt(o),i.c(),I(i,1),i.m(e.parentNode,e)):i&&(mt(),X(i,1,1,()=>{i=null}),yt())},i(o){n||(I(i),n=!0)},o(o){X(i),n=!1},d(o){i&&i.d(o),o&&D(e)}}}function Qt(t,e,n){let i;return it(t,T,o=>n(0,i=o)),[i]}var Mt=class extends Q{constructor(e){super();bt(this,e,Qt,Zt,O,{},Xt)}},Ct=Mt;var Yt={};window.ipcCallbacks=[];window.ipcCallbackNames=[];window.awaitIPC=(t,e)=>{if(!window.ipcCallbacks)return e;F("Queuing '"+t+"' for execution once ipc ready."),window.ipcCallbackNames.push(t),window.ipcCallbacks.push(e)};window.addEventListener("DOMContentLoaded",()=>{Yt.overlay=new Ct({target:document.body,anchor:document.querySelector("#wails-spinner")})});var d=null,St;window.onbeforeunload=function(){d&&(d.onclose=function(){},d.close(),d=null)};$t();function te(){window.WailsInvoke=t=>{d.send(t)};for(let t=0;t", + "license": "ISC", + "devDependencies": { + "esbuild": "^0.12.17", + "npm-run-all": "^4.1.5", + "svelte": "^3.42.2" + } +} diff --git a/v2/internal/frontend/runtime/runtime_dev_desktop.go b/v2/internal/frontend/runtime/runtime_dev_desktop.go new file mode 100644 index 000000000..c0dcb1fc5 --- /dev/null +++ b/v2/internal/frontend/runtime/runtime_dev_desktop.go @@ -0,0 +1,8 @@ +//go:build dev || bindings || (!dev && !production && !bindings) + +package runtime + +import _ "embed" + +//go:embed runtime_dev_desktop.js +var RuntimeDesktopJS []byte diff --git a/v2/internal/frontend/runtime/runtime_dev_desktop.js b/v2/internal/frontend/runtime/runtime_dev_desktop.js new file mode 100644 index 000000000..9106a51cf --- /dev/null +++ b/v2/internal/frontend/runtime/runtime_dev_desktop.js @@ -0,0 +1,369 @@ +(() => { + var __defProp = Object.defineProperty; + var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); + var __export = (target, all) => { + __markAsModule(target); + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); + }; + + // desktop/log.js + var log_exports = {}; + __export(log_exports, { + LogDebug: () => LogDebug, + LogError: () => LogError, + LogFatal: () => LogFatal, + LogInfo: () => LogInfo, + LogLevel: () => LogLevel, + LogPrint: () => LogPrint, + LogTrace: () => LogTrace, + LogWarning: () => LogWarning, + SetLogLevel: () => SetLogLevel + }); + function sendLogMessage(level, message) { + window.WailsInvoke("L" + level + message); + } + function LogTrace(message) { + sendLogMessage("T", message); + } + function LogPrint(message) { + sendLogMessage("P", message); + } + function LogDebug(message) { + sendLogMessage("D", message); + } + function LogInfo(message) { + sendLogMessage("I", message); + } + function LogWarning(message) { + sendLogMessage("W", message); + } + function LogError(message) { + sendLogMessage("E", message); + } + function LogFatal(message) { + sendLogMessage("F", message); + } + function SetLogLevel(loglevel) { + sendLogMessage("S", loglevel); + } + var LogLevel = { + TRACE: 1, + DEBUG: 2, + INFO: 3, + WARNING: 4, + ERROR: 5 + }; + + // desktop/events.js + var Listener = class { + constructor(callback, maxCallbacks) { + maxCallbacks = maxCallbacks || -1; + this.Callback = (data) => { + callback.apply(null, data); + if (maxCallbacks === -1) { + return false; + } + maxCallbacks -= 1; + return maxCallbacks === 0; + }; + } + }; + var eventListeners = {}; + function EventsOnMultiple(eventName, callback, maxCallbacks) { + eventListeners[eventName] = eventListeners[eventName] || []; + const thisListener = new Listener(callback, maxCallbacks); + eventListeners[eventName].push(thisListener); + } + function EventsOn(eventName, callback) { + EventsOnMultiple(eventName, callback, -1); + } + function EventsOnce(eventName, callback) { + EventsOnMultiple(eventName, callback, 1); + } + function notifyListeners(eventData) { + let eventName = eventData.name; + if (eventListeners[eventName]) { + const newEventListenerList = eventListeners[eventName].slice(); + for (let count = 0; count < eventListeners[eventName].length; count += 1) { + const listener = eventListeners[eventName][count]; + let data = eventData.data; + const destroy = listener.Callback(data); + if (destroy) { + newEventListenerList.splice(count, 1); + } + } + eventListeners[eventName] = newEventListenerList; + } + } + function EventsNotify(notifyMessage) { + let message; + try { + message = JSON.parse(notifyMessage); + } catch (e) { + const error = "Invalid JSON passed to Notify: " + notifyMessage; + throw new Error(error); + } + notifyListeners(message); + } + function EventsEmit(eventName) { + const payload = { + name: eventName, + data: [].slice.apply(arguments).slice(1) + }; + notifyListeners(payload); + window.WailsInvoke("EE" + JSON.stringify(payload)); + } + function EventsOff(eventName) { + eventListeners.delete(eventName); + window.WailsInvoke("EX" + eventName); + } + + // desktop/calls.js + var callbacks = {}; + 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 Call(name, args, timeout) { + if (timeout == null) { + timeout = 0; + } + return new Promise(function(resolve, reject) { + var callbackID; + do { + callbackID = name + "-" + randomFunc(); + } while (callbacks[callbackID]); + var timeoutHandle; + if (timeout > 0) { + timeoutHandle = setTimeout(function() { + reject(Error("Call to " + name + " timed out. Request ID: " + callbackID)); + }, timeout); + } + callbacks[callbackID] = { + timeoutHandle, + reject, + resolve + }; + try { + const payload = { + name, + args, + callbackID + }; + window.WailsInvoke("C" + JSON.stringify(payload)); + } catch (e) { + console.error(e); + } + }); + } + function Callback(incomingMessage) { + var message; + try { + message = JSON.parse(incomingMessage); + } catch (e) { + const error = `Invalid JSON passed to callback: ${e.message}. Message: ${incomingMessage}`; + wails.LogDebug(error); + throw new Error(error); + } + var callbackID = message.callbackid; + var callbackData = callbacks[callbackID]; + if (!callbackData) { + const error = `Callback '${callbackID}' not registered!!!`; + console.error(error); + throw new Error(error); + } + clearTimeout(callbackData.timeoutHandle); + delete callbacks[callbackID]; + if (message.error) { + callbackData.reject(message.error); + } else { + callbackData.resolve(message.result); + } + } + + // desktop/bindings.js + window.go = {}; + function SetBindings(bindingsMap) { + try { + bindingsMap = JSON.parse(bindingsMap); + } catch (e) { + console.error(e); + } + window.go = window.go || {}; + Object.keys(bindingsMap).forEach((packageName) => { + window.go[packageName] = window.go[packageName] || {}; + Object.keys(bindingsMap[packageName]).forEach((structName) => { + window.go[packageName][structName] = window.go[packageName][structName] || {}; + Object.keys(bindingsMap[packageName][structName]).forEach((methodName) => { + window.go[packageName][structName][methodName] = function() { + let timeout = 0; + function dynamic() { + const args = [].slice.call(arguments); + return Call([packageName, structName, methodName].join("."), args, timeout); + } + dynamic.setTimeout = function(newTimeout) { + timeout = newTimeout; + }; + dynamic.getTimeout = function() { + return timeout; + }; + return dynamic; + }(); + }); + }); + }); + } + + // desktop/window.js + var window_exports = {}; + __export(window_exports, { + WindowCenter: () => WindowCenter, + WindowFullscreen: () => WindowFullscreen, + WindowGetPosition: () => WindowGetPosition, + WindowGetSize: () => WindowGetSize, + WindowHide: () => WindowHide, + WindowMaximise: () => WindowMaximise, + WindowMinimise: () => WindowMinimise, + WindowReload: () => WindowReload, + WindowSetMaxSize: () => WindowSetMaxSize, + WindowSetMinSize: () => WindowSetMinSize, + WindowSetPosition: () => WindowSetPosition, + WindowSetRGBA: () => WindowSetRGBA, + WindowSetSize: () => WindowSetSize, + WindowSetTitle: () => WindowSetTitle, + WindowShow: () => WindowShow, + WindowUnFullscreen: () => WindowUnFullscreen, + WindowUnmaximise: () => WindowUnmaximise, + WindowUnminimise: () => WindowUnminimise + }); + function WindowReload() { + window.location.reload(); + } + function WindowCenter() { + window.WailsInvoke("Wc"); + } + function WindowSetTitle(title) { + window.WailsInvoke("WT" + title); + } + function WindowFullscreen() { + window.WailsInvoke("WF"); + } + function WindowUnFullscreen() { + window.WailsInvoke("Wf"); + } + function WindowSetSize(width, height) { + window.WailsInvoke("Ws:" + width + ":" + height); + } + function WindowGetSize() { + return Call(":wails:WindowGetSize"); + } + function WindowSetMaxSize(width, height) { + window.WailsInvoke("WZ:" + width + ":" + height); + } + function WindowSetMinSize(width, height) { + window.WailsInvoke("Wz:" + width + ":" + height); + } + function WindowSetPosition(x, y) { + window.WailsInvoke("Wp:" + x + ":" + y); + } + function WindowGetPosition() { + return Call(":wails:WindowGetPos"); + } + function WindowHide() { + window.WailsInvoke("WH"); + } + function WindowShow() { + window.WailsInvoke("WS"); + } + function WindowMaximise() { + window.WailsInvoke("WM"); + } + function WindowUnmaximise() { + window.WailsInvoke("WU"); + } + function WindowMinimise() { + window.WailsInvoke("Wm"); + } + function WindowUnminimise() { + window.WailsInvoke("Wu"); + } + function WindowSetRGBA(RGBA) { + let rgba = JSON.stringify(RGBA); + window.WailsInvoke("Wr:" + rgba); + } + + // desktop/browser.js + var browser_exports = {}; + __export(browser_exports, { + BrowserOpenURL: () => BrowserOpenURL + }); + function BrowserOpenURL(url) { + window.WailsInvoke("BO:" + url); + } + + // desktop/main.js + function Quit() { + window.WailsInvoke("Q"); + } + window.runtime = { + ...log_exports, + ...window_exports, + ...browser_exports, + EventsOn, + EventsOnce, + EventsOnMultiple, + EventsEmit, + EventsOff, + Quit + }; + window.wails = { + Callback, + EventsNotify, + SetBindings, + eventListeners, + callbacks, + flags: { + disableScrollbarDrag: false, + disableWailsDefaultContextMenu: false + } + }; + window.wails.SetBindings(window.wailsbindings); + delete window.wails.SetBindings; + if (true) { + delete window.wailsbindings; + } + window.addEventListener("mousedown", (e) => { + let currentElement = e.target; + while (currentElement != null) { + if (currentElement.hasAttribute("data-wails-no-drag")) { + break; + } else if (currentElement.hasAttribute("data-wails-drag")) { + if (window.wails.flags.disableScrollbarDrag) { + if (e.offsetX > e.target.clientWidth || e.offsetY > e.target.clientHeight) { + break; + } + } + window.WailsInvoke("drag"); + e.preventDefault(); + break; + } + currentElement = currentElement.parentElement; + } + }); + window.addEventListener("contextmenu", function(e) { + if (window.wails.flags.disableWailsDefaultContextMenu) { + e.preventDefault(); + } + }); +})(); +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiZGVza3RvcC9sb2cuanMiLCAiZGVza3RvcC9ldmVudHMuanMiLCAiZGVza3RvcC9jYWxscy5qcyIsICJkZXNrdG9wL2JpbmRpbmdzLmpzIiwgImRlc2t0b3Avd2luZG93LmpzIiwgImRlc2t0b3AvYnJvd3Nlci5qcyIsICJkZXNrdG9wL21haW4uanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qXG4gXyAgICAgICBfXyAgICAgIF8gX19cbnwgfCAgICAgLyAvX19fIF8oXykgL19fX19cbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxufF9fL3xfXy9cXF9fLF8vXy9fL19fX18vXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XG4qL1xuXG4vKiBqc2hpbnQgZXN2ZXJzaW9uOiA2ICovXG5cbi8qKlxuICogU2VuZHMgYSBsb2cgbWVzc2FnZSB0byB0aGUgYmFja2VuZCB3aXRoIHRoZSBnaXZlbiBsZXZlbCArIG1lc3NhZ2VcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gbGV2ZWxcbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlXG4gKi9cbmZ1bmN0aW9uIHNlbmRMb2dNZXNzYWdlKGxldmVsLCBtZXNzYWdlKSB7XG5cblx0Ly8gTG9nIE1lc3NhZ2UgZm9ybWF0OlxuXHQvLyBsW3R5cGVdW21lc3NhZ2VdXG5cdHdpbmRvdy5XYWlsc0ludm9rZSgnTCcgKyBsZXZlbCArIG1lc3NhZ2UpO1xufVxuXG4vKipcbiAqIExvZyB0aGUgZ2l2ZW4gdHJhY2UgbWVzc2FnZSB3aXRoIHRoZSBiYWNrZW5kXG4gKlxuICogQGV4cG9ydFxuICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIExvZ1RyYWNlKG1lc3NhZ2UpIHtcblx0c2VuZExvZ01lc3NhZ2UoJ1QnLCBtZXNzYWdlKTtcbn1cblxuLyoqXG4gKiBMb2cgdGhlIGdpdmVuIG1lc3NhZ2Ugd2l0aCB0aGUgYmFja2VuZFxuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBMb2dQcmludChtZXNzYWdlKSB7XG5cdHNlbmRMb2dNZXNzYWdlKCdQJywgbWVzc2FnZSk7XG59XG5cbi8qKlxuICogTG9nIHRoZSBnaXZlbiBkZWJ1ZyBtZXNzYWdlIHdpdGggdGhlIGJhY2tlbmRcbiAqXG4gKiBAZXhwb3J0XG4gKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZVxuICovXG5leHBvcnQgZnVuY3Rpb24gTG9nRGVidWcobWVzc2FnZSkge1xuXHRzZW5kTG9nTWVzc2FnZSgnRCcsIG1lc3NhZ2UpO1xufVxuXG4vKipcbiAqIExvZyB0aGUgZ2l2ZW4gaW5mbyBtZXNzYWdlIHdpdGggdGhlIGJhY2tlbmRcbiAqXG4gKiBAZXhwb3J0XG4gKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZVxuICovXG5leHBvcnQgZnVuY3Rpb24gTG9nSW5mbyhtZXNzYWdlKSB7XG5cdHNlbmRMb2dNZXNzYWdlKCdJJywgbWVzc2FnZSk7XG59XG5cbi8qKlxuICogTG9nIHRoZSBnaXZlbiB3YXJuaW5nIG1lc3NhZ2Ugd2l0aCB0aGUgYmFja2VuZFxuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBMb2dXYXJuaW5nKG1lc3NhZ2UpIHtcblx0c2VuZExvZ01lc3NhZ2UoJ1cnLCBtZXNzYWdlKTtcbn1cblxuLyoqXG4gKiBMb2cgdGhlIGdpdmVuIGVycm9yIG1lc3NhZ2Ugd2l0aCB0aGUgYmFja2VuZFxuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBMb2dFcnJvcihtZXNzYWdlKSB7XG5cdHNlbmRMb2dNZXNzYWdlKCdFJywgbWVzc2FnZSk7XG59XG5cbi8qKlxuICogTG9nIHRoZSBnaXZlbiBmYXRhbCBtZXNzYWdlIHdpdGggdGhlIGJhY2tlbmRcbiAqXG4gKiBAZXhwb3J0XG4gKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZVxuICovXG5leHBvcnQgZnVuY3Rpb24gTG9nRmF0YWwobWVzc2FnZSkge1xuXHRzZW5kTG9nTWVzc2FnZSgnRicsIG1lc3NhZ2UpO1xufVxuXG4vKipcbiAqIFNldHMgdGhlIExvZyBsZXZlbCB0byB0aGUgZ2l2ZW4gbG9nIGxldmVsXG4gKlxuICogQGV4cG9ydFxuICogQHBhcmFtIHtudW1iZXJ9IGxvZ2xldmVsXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBTZXRMb2dMZXZlbChsb2dsZXZlbCkge1xuXHRzZW5kTG9nTWVzc2FnZSgnUycsIGxvZ2xldmVsKTtcbn1cblxuLy8gTG9nIGxldmVsc1xuZXhwb3J0IGNvbnN0IExvZ0xldmVsID0ge1xuXHRUUkFDRTogMSxcblx0REVCVUc6IDIsXG5cdElORk86IDMsXG5cdFdBUk5JTkc6IDQsXG5cdEVSUk9SOiA1LFxufTtcbiIsICIvKlxuIF8gICAgICAgX18gICAgICBfIF9fXG58IHwgICAgIC8gL19fXyBfKF8pIC9fX19fXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXG58IHwvIHwvIC8gL18vIC8gLyAoX18gIClcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xuKGMpIExlYSBBbnRob255IDIwMTktcHJlc2VudFxuKi9cbi8qIGpzaGludCBlc3ZlcnNpb246IDYgKi9cblxuLy8gRGVmaW5lcyBhIHNpbmdsZSBsaXN0ZW5lciB3aXRoIGEgbWF4aW11bSBudW1iZXIgb2YgdGltZXMgdG8gY2FsbGJhY2tcblxuLyoqXG4gKiBUaGUgTGlzdGVuZXIgY2xhc3MgZGVmaW5lcyBhIGxpc3RlbmVyISA6LSlcbiAqXG4gKiBAY2xhc3MgTGlzdGVuZXJcbiAqL1xuY2xhc3MgTGlzdGVuZXIge1xuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgTGlzdGVuZXIuXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2tcbiAgICAgKiBAcGFyYW0ge251bWJlcn0gbWF4Q2FsbGJhY2tzXG4gICAgICogQG1lbWJlcm9mIExpc3RlbmVyXG4gICAgICovXG4gICAgY29uc3RydWN0b3IoY2FsbGJhY2ssIG1heENhbGxiYWNrcykge1xuICAgICAgICAvLyBEZWZhdWx0IG9mIC0xIG1lYW5zIGluZmluaXRlXG4gICAgICAgIG1heENhbGxiYWNrcyA9IG1heENhbGxiYWNrcyB8fCAtMTtcbiAgICAgICAgLy8gQ2FsbGJhY2sgaW52b2tlcyB0aGUgY2FsbGJhY2sgd2l0aCB0aGUgZ2l2ZW4gZGF0YVxuICAgICAgICAvLyBSZXR1cm5zIHRydWUgaWYgdGhpcyBsaXN0ZW5lciBzaG91bGQgYmUgZGVzdHJveWVkXG4gICAgICAgIHRoaXMuQ2FsbGJhY2sgPSAoZGF0YSkgPT4ge1xuICAgICAgICAgICAgY2FsbGJhY2suYXBwbHkobnVsbCwgZGF0YSk7XG4gICAgICAgICAgICAvLyBJZiBtYXhDYWxsYmFja3MgaXMgaW5maW5pdGUsIHJldHVybiBmYWxzZSAoZG8gbm90IGRlc3Ryb3kpXG4gICAgICAgICAgICBpZiAobWF4Q2FsbGJhY2tzID09PSAtMSkge1xuICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIERlY3JlbWVudCBtYXhDYWxsYmFja3MuIFJldHVybiB0cnVlIGlmIG5vdyAwLCBvdGhlcndpc2UgZmFsc2VcbiAgICAgICAgICAgIG1heENhbGxiYWNrcyAtPSAxO1xuICAgICAgICAgICAgcmV0dXJuIG1heENhbGxiYWNrcyA9PT0gMDtcbiAgICAgICAgfTtcbiAgICB9XG59XG5cbmV4cG9ydCBjb25zdCBldmVudExpc3RlbmVycyA9IHt9O1xuXG4vKipcbiAqIFJlZ2lzdGVycyBhbiBldmVudCBsaXN0ZW5lciB0aGF0IHdpbGwgYmUgaW52b2tlZCBgbWF4Q2FsbGJhY2tzYCB0aW1lcyBiZWZvcmUgYmVpbmcgZGVzdHJveWVkXG4gKlxuICogQGV4cG9ydFxuICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZVxuICogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2tcbiAqIEBwYXJhbSB7bnVtYmVyfSBtYXhDYWxsYmFja3NcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEV2ZW50c09uTXVsdGlwbGUoZXZlbnROYW1lLCBjYWxsYmFjaywgbWF4Q2FsbGJhY2tzKSB7XG4gICAgZXZlbnRMaXN0ZW5lcnNbZXZlbnROYW1lXSA9IGV2ZW50TGlzdGVuZXJzW2V2ZW50TmFtZV0gfHwgW107XG4gICAgY29uc3QgdGhpc0xpc3RlbmVyID0gbmV3IExpc3RlbmVyKGNhbGxiYWNrLCBtYXhDYWxsYmFja3MpO1xuICAgIGV2ZW50TGlzdGVuZXJzW2V2ZW50TmFtZV0ucHVzaCh0aGlzTGlzdGVuZXIpO1xufVxuXG4vKipcbiAqIFJlZ2lzdGVycyBhbiBldmVudCBsaXN0ZW5lciB0aGF0IHdpbGwgYmUgaW52b2tlZCBldmVyeSB0aW1lIHRoZSBldmVudCBpcyBlbWl0dGVkXG4gKlxuICogQGV4cG9ydFxuICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50TmFtZVxuICogQHBhcmFtIHtmdW5jdGlvbn0gY2FsbGJhY2tcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEV2ZW50c09uKGV2ZW50TmFtZSwgY2FsbGJhY2spIHtcbiAgICBFdmVudHNPbk11bHRpcGxlKGV2ZW50TmFtZSwgY2FsbGJhY2ssIC0xKTtcbn1cblxuLyoqXG4gKiBSZWdpc3RlcnMgYW4gZXZlbnQgbGlzdGVuZXIgdGhhdCB3aWxsIGJlIGludm9rZWQgb25jZSB0aGVuIGRlc3Ryb3llZFxuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7c3RyaW5nfSBldmVudE5hbWVcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGNhbGxiYWNrXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBFdmVudHNPbmNlKGV2ZW50TmFtZSwgY2FsbGJhY2spIHtcbiAgICBFdmVudHNPbk11bHRpcGxlKGV2ZW50TmFtZSwgY2FsbGJhY2ssIDEpO1xufVxuXG5mdW5jdGlvbiBub3RpZnlMaXN0ZW5lcnMoZXZlbnREYXRhKSB7XG5cbiAgICAvLyBHZXQgdGhlIGV2ZW50IG5hbWVcbiAgICBsZXQgZXZlbnROYW1lID0gZXZlbnREYXRhLm5hbWU7XG5cbiAgICAvLyBDaGVjayBpZiB3ZSBoYXZlIGFueSBsaXN0ZW5lcnMgZm9yIHRoaXMgZXZlbnRcbiAgICBpZiAoZXZlbnRMaXN0ZW5lcnNbZXZlbnROYW1lXSkge1xuXG4gICAgICAgIC8vIEtlZXAgYSBsaXN0IG9mIGxpc3RlbmVyIGluZGV4ZXMgdG8gZGVzdHJveVxuICAgICAgICBjb25zdCBuZXdFdmVudExpc3RlbmVyTGlzdCA9IGV2ZW50TGlzdGVuZXJzW2V2ZW50TmFtZV0uc2xpY2UoKTtcblxuICAgICAgICAvLyBJdGVyYXRlIGxpc3RlbmVyc1xuICAgICAgICBmb3IgKGxldCBjb3VudCA9IDA7IGNvdW50IDwgZXZlbnRMaXN0ZW5lcnNbZXZlbnROYW1lXS5sZW5ndGg7IGNvdW50ICs9IDEpIHtcblxuICAgICAgICAgICAgLy8gR2V0IG5leHQgbGlzdGVuZXJcbiAgICAgICAgICAgIGNvbnN0IGxpc3RlbmVyID0gZXZlbnRMaXN0ZW5lcnNbZXZlbnROYW1lXVtjb3VudF07XG5cbiAgICAgICAgICAgIGxldCBkYXRhID0gZXZlbnREYXRhLmRhdGE7XG5cbiAgICAgICAgICAgIC8vIERvIHRoZSBjYWxsYmFja1xuICAgICAgICAgICAgY29uc3QgZGVzdHJveSA9IGxpc3RlbmVyLkNhbGxiYWNrKGRhdGEpO1xuICAgICAgICAgICAgaWYgKGRlc3Ryb3kpIHtcbiAgICAgICAgICAgICAgICAvLyBpZiB0aGUgbGlzdGVuZXIgaW5kaWNhdGVkIHRvIGRlc3Ryb3kgaXRzZWxmLCBhZGQgaXQgdG8gdGhlIGRlc3Ryb3kgbGlzdFxuICAgICAgICAgICAgICAgIG5ld0V2ZW50TGlzdGVuZXJMaXN0LnNwbGljZShjb3VudCwgMSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBVcGRhdGUgY2FsbGJhY2tzIHdpdGggbmV3IGxpc3Qgb2YgbGlzdGVuZXJzXG4gICAgICAgIGV2ZW50TGlzdGVuZXJzW2V2ZW50TmFtZV0gPSBuZXdFdmVudExpc3RlbmVyTGlzdDtcbiAgICB9XG59XG5cbi8qKlxuICogTm90aWZ5IGluZm9ybXMgZnJvbnRlbmQgbGlzdGVuZXJzIHRoYXQgYW4gZXZlbnQgd2FzIGVtaXR0ZWQgd2l0aCB0aGUgZ2l2ZW4gZGF0YVxuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7c3RyaW5nfSBub3RpZnlNZXNzYWdlIC0gZW5jb2RlZCBub3RpZmljYXRpb24gbWVzc2FnZVxuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBFdmVudHNOb3RpZnkobm90aWZ5TWVzc2FnZSkge1xuICAgIC8vIFBhcnNlIHRoZSBtZXNzYWdlXG4gICAgbGV0IG1lc3NhZ2U7XG4gICAgdHJ5IHtcbiAgICAgICAgbWVzc2FnZSA9IEpTT04ucGFyc2Uobm90aWZ5TWVzc2FnZSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBjb25zdCBlcnJvciA9ICdJbnZhbGlkIEpTT04gcGFzc2VkIHRvIE5vdGlmeTogJyArIG5vdGlmeU1lc3NhZ2U7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihlcnJvcik7XG4gICAgfVxuICAgIG5vdGlmeUxpc3RlbmVycyhtZXNzYWdlKTtcbn1cblxuLyoqXG4gKiBFbWl0IGFuIGV2ZW50IHdpdGggdGhlIGdpdmVuIG5hbWUgYW5kIGRhdGFcbiAqXG4gKiBAZXhwb3J0XG4gKiBAcGFyYW0ge3N0cmluZ30gZXZlbnROYW1lXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBFdmVudHNFbWl0KGV2ZW50TmFtZSkge1xuXG4gICAgY29uc3QgcGF5bG9hZCA9IHtcbiAgICAgICAgbmFtZTogZXZlbnROYW1lLFxuICAgICAgICBkYXRhOiBbXS5zbGljZS5hcHBseShhcmd1bWVudHMpLnNsaWNlKDEpLFxuICAgIH07XG5cbiAgICAvLyBOb3RpZnkgSlMgbGlzdGVuZXJzXG4gICAgbm90aWZ5TGlzdGVuZXJzKHBheWxvYWQpO1xuXG4gICAgLy8gTm90aWZ5IEdvIGxpc3RlbmVyc1xuICAgIHdpbmRvdy5XYWlsc0ludm9rZSgnRUUnICsgSlNPTi5zdHJpbmdpZnkocGF5bG9hZCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gRXZlbnRzT2ZmKGV2ZW50TmFtZSkge1xuICAgIC8vIFJlbW92ZSBsb2NhbCBsaXN0ZW5lcnNcbiAgICBldmVudExpc3RlbmVycy5kZWxldGUoZXZlbnROYW1lKTtcblxuICAgIC8vIE5vdGlmeSBHbyBsaXN0ZW5lcnNcbiAgICB3aW5kb3cuV2FpbHNJbnZva2UoJ0VYJyArIGV2ZW50TmFtZSk7XG59IiwgIi8qXG4gXyAgICAgICBfXyAgICAgIF8gX19cbnwgfCAgICAgLyAvX19fIF8oXykgL19fX19cbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKVxufF9fL3xfXy9cXF9fLF8vXy9fL19fX18vXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XG4qL1xuLyoganNoaW50IGVzdmVyc2lvbjogNiAqL1xuXG5leHBvcnQgY29uc3QgY2FsbGJhY2tzID0ge307XG5cbi8qKlxuICogUmV0dXJucyBhIG51bWJlciBmcm9tIHRoZSBuYXRpdmUgYnJvd3NlciByYW5kb20gZnVuY3Rpb25cbiAqXG4gKiBAcmV0dXJucyBudW1iZXJcbiAqL1xuZnVuY3Rpb24gY3J5cHRvUmFuZG9tKCkge1xuXHR2YXIgYXJyYXkgPSBuZXcgVWludDMyQXJyYXkoMSk7XG5cdHJldHVybiB3aW5kb3cuY3J5cHRvLmdldFJhbmRvbVZhbHVlcyhhcnJheSlbMF07XG59XG5cbi8qKlxuICogUmV0dXJucyBhIG51bWJlciB1c2luZyBkYSBvbGQtc2tvb2wgTWF0aC5SYW5kb21cbiAqIEkgbGlrZXMgdG8gY2FsbCBpdCBMT0xSYW5kb21cbiAqXG4gKiBAcmV0dXJucyBudW1iZXJcbiAqL1xuZnVuY3Rpb24gYmFzaWNSYW5kb20oKSB7XG5cdHJldHVybiBNYXRoLnJhbmRvbSgpICogOTAwNzE5OTI1NDc0MDk5MTtcbn1cblxuLy8gUGljayBhIHJhbmRvbSBudW1iZXIgZnVuY3Rpb24gYmFzZWQgb24gYnJvd3NlciBjYXBhYmlsaXR5XG52YXIgcmFuZG9tRnVuYztcbmlmICh3aW5kb3cuY3J5cHRvKSB7XG5cdHJhbmRvbUZ1bmMgPSBjcnlwdG9SYW5kb207XG59IGVsc2Uge1xuXHRyYW5kb21GdW5jID0gYmFzaWNSYW5kb207XG59XG5cblxuLyoqXG4gKiBDYWxsIHNlbmRzIGEgbWVzc2FnZSB0byB0aGUgYmFja2VuZCB0byBjYWxsIHRoZSBiaW5kaW5nIHdpdGggdGhlXG4gKiBnaXZlbiBkYXRhLiBBIHByb21pc2UgaXMgcmV0dXJuZWQgYW5kIHdpbGwgYmUgY29tcGxldGVkIHdoZW4gdGhlXG4gKiBiYWNrZW5kIHJlc3BvbmRzLiBUaGlzIHdpbGwgYmUgcmVzb2x2ZWQgd2hlbiB0aGUgY2FsbCB3YXMgc3VjY2Vzc2Z1bFxuICogb3IgcmVqZWN0ZWQgaWYgYW4gZXJyb3IgaXMgcGFzc2VkIGJhY2suXG4gKiBUaGVyZSBpcyBhIHRpbWVvdXQgbWVjaGFuaXNtLiBJZiB0aGUgY2FsbCBkb2Vzbid0IHJlc3BvbmQgaW4gdGhlIGdpdmVuXG4gKiB0aW1lIChpbiBtaWxsaXNlY29uZHMpIHRoZW4gdGhlIHByb21pc2UgaXMgcmVqZWN0ZWQuXG4gKlxuICogQGV4cG9ydFxuICogQHBhcmFtIHtzdHJpbmd9IG5hbWVcbiAqIEBwYXJhbSB7YW55PX0gYXJnc1xuICogQHBhcmFtIHtudW1iZXI9fSB0aW1lb3V0XG4gKiBAcmV0dXJuc1xuICovXG5leHBvcnQgZnVuY3Rpb24gQ2FsbChuYW1lLCBhcmdzLCB0aW1lb3V0KSB7XG5cblx0Ly8gVGltZW91dCBpbmZpbml0ZSBieSBkZWZhdWx0XG5cdGlmICh0aW1lb3V0ID09IG51bGwpIHtcblx0XHR0aW1lb3V0ID0gMDtcblx0fVxuXG5cdC8vIENyZWF0ZSBhIHByb21pc2Vcblx0cmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcblxuXHRcdC8vIENyZWF0ZSBhIHVuaXF1ZSBjYWxsYmFja0lEXG5cdFx0dmFyIGNhbGxiYWNrSUQ7XG5cdFx0ZG8ge1xuXHRcdFx0Y2FsbGJhY2tJRCA9IG5hbWUgKyAnLScgKyByYW5kb21GdW5jKCk7XG5cdFx0fSB3aGlsZSAoY2FsbGJhY2tzW2NhbGxiYWNrSURdKTtcblxuXHRcdHZhciB0aW1lb3V0SGFuZGxlO1xuXHRcdC8vIFNldCB0aW1lb3V0XG5cdFx0aWYgKHRpbWVvdXQgPiAwKSB7XG5cdFx0XHR0aW1lb3V0SGFuZGxlID0gc2V0VGltZW91dChmdW5jdGlvbiAoKSB7XG5cdFx0XHRcdHJlamVjdChFcnJvcignQ2FsbCB0byAnICsgbmFtZSArICcgdGltZWQgb3V0LiBSZXF1ZXN0IElEOiAnICsgY2FsbGJhY2tJRCkpO1xuXHRcdFx0fSwgdGltZW91dCk7XG5cdFx0fVxuXG5cdFx0Ly8gU3RvcmUgY2FsbGJhY2tcblx0XHRjYWxsYmFja3NbY2FsbGJhY2tJRF0gPSB7XG5cdFx0XHR0aW1lb3V0SGFuZGxlOiB0aW1lb3V0SGFuZGxlLFxuXHRcdFx0cmVqZWN0OiByZWplY3QsXG5cdFx0XHRyZXNvbHZlOiByZXNvbHZlXG5cdFx0fTtcblxuXHRcdHRyeSB7XG5cdFx0XHRjb25zdCBwYXlsb2FkID0ge1xuXHRcdFx0XHRuYW1lLFxuXHRcdFx0XHRhcmdzLFxuXHRcdFx0XHRjYWxsYmFja0lELFxuXHRcdFx0fTtcblxuXHRcdFx0Ly8gTWFrZSB0aGUgY2FsbFxuXHRcdFx0d2luZG93LldhaWxzSW52b2tlKCdDJyArIEpTT04uc3RyaW5naWZ5KHBheWxvYWQpKTtcblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHQvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmVcblx0XHRcdGNvbnNvbGUuZXJyb3IoZSk7XG5cdFx0fVxuXHR9KTtcbn1cblxuXG5cbi8qKlxuICogQ2FsbGVkIGJ5IHRoZSBiYWNrZW5kIHRvIHJldHVybiBkYXRhIHRvIGEgcHJldmlvdXNseSBjYWxsZWRcbiAqIGJpbmRpbmcgaW52b2NhdGlvblxuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbmNvbWluZ01lc3NhZ2VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIENhbGxiYWNrKGluY29taW5nTWVzc2FnZSkge1xuXHQvLyBEZWNvZGUgdGhlIG1lc3NhZ2UgLSBDcmVkaXQ6IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vYS8xMzg2NTY4MFxuXHQvL2luY29taW5nTWVzc2FnZSA9IGRlY29kZVVSSUNvbXBvbmVudChpbmNvbWluZ01lc3NhZ2UucmVwbGFjZSgvXFxzKy9nLCAnJykucmVwbGFjZSgvWzAtOWEtZl17Mn0vZywgJyUkJicpKTtcblxuXHQvLyBQYXJzZSB0aGUgbWVzc2FnZVxuXHR2YXIgbWVzc2FnZTtcblx0dHJ5IHtcblx0XHRtZXNzYWdlID0gSlNPTi5wYXJzZShpbmNvbWluZ01lc3NhZ2UpO1xuXHR9IGNhdGNoIChlKSB7XG5cdFx0Y29uc3QgZXJyb3IgPSBgSW52YWxpZCBKU09OIHBhc3NlZCB0byBjYWxsYmFjazogJHtlLm1lc3NhZ2V9LiBNZXNzYWdlOiAke2luY29taW5nTWVzc2FnZX1gO1xuXHRcdHdhaWxzLkxvZ0RlYnVnKGVycm9yKTtcblx0XHR0aHJvdyBuZXcgRXJyb3IoZXJyb3IpO1xuXHR9XG5cdHZhciBjYWxsYmFja0lEID0gbWVzc2FnZS5jYWxsYmFja2lkO1xuXHR2YXIgY2FsbGJhY2tEYXRhID0gY2FsbGJhY2tzW2NhbGxiYWNrSURdO1xuXHRpZiAoIWNhbGxiYWNrRGF0YSkge1xuXHRcdGNvbnN0IGVycm9yID0gYENhbGxiYWNrICcke2NhbGxiYWNrSUR9JyBub3QgcmVnaXN0ZXJlZCEhIWA7XG5cdFx0Y29uc29sZS5lcnJvcihlcnJvcik7IC8vIGVzbGludC1kaXNhYmxlLWxpbmVcblx0XHR0aHJvdyBuZXcgRXJyb3IoZXJyb3IpO1xuXHR9XG5cdGNsZWFyVGltZW91dChjYWxsYmFja0RhdGEudGltZW91dEhhbmRsZSk7XG5cblx0ZGVsZXRlIGNhbGxiYWNrc1tjYWxsYmFja0lEXTtcblxuXHRpZiAobWVzc2FnZS5lcnJvcikge1xuXHRcdGNhbGxiYWNrRGF0YS5yZWplY3QobWVzc2FnZS5lcnJvcik7XG5cdH0gZWxzZSB7XG5cdFx0Y2FsbGJhY2tEYXRhLnJlc29sdmUobWVzc2FnZS5yZXN1bHQpO1xuXHR9XG59XG4iLCAiLypcbiBfICAgICAgIF9fICAgICAgXyBfXyAgICBcbnwgfCAgICAgLyAvX19fIF8oXykgL19fX19cbnwgfCAvfCAvIC8gX18gYC8gLyAvIF9fXy9cbnwgfC8gfC8gLyAvXy8gLyAvIChfXyAgKSBcbnxfXy98X18vXFxfXyxfL18vXy9fX19fLyAgXG5UaGUgZWxlY3Ryb24gYWx0ZXJuYXRpdmUgZm9yIEdvXG4oYykgTGVhIEFudGhvbnkgMjAxOS1wcmVzZW50XG4qL1xuLyoganNoaW50IGVzdmVyc2lvbjogNiAqL1xuXG5pbXBvcnQge0NhbGx9IGZyb20gJy4vY2FsbHMnO1xuXG4vLyBUaGlzIGlzIHdoZXJlIHdlIGJpbmQgZ28gbWV0aG9kIHdyYXBwZXJzXG53aW5kb3cuZ28gPSB7fTtcblxuZXhwb3J0IGZ1bmN0aW9uIFNldEJpbmRpbmdzKGJpbmRpbmdzTWFwKSB7XG5cdHRyeSB7XG5cdFx0YmluZGluZ3NNYXAgPSBKU09OLnBhcnNlKGJpbmRpbmdzTWFwKTtcblx0fSBjYXRjaCAoZSkge1xuXHRcdGNvbnNvbGUuZXJyb3IoZSk7XG5cdH1cblxuXHQvLyBJbml0aWFsaXNlIHRoZSBiaW5kaW5ncyBtYXBcblx0d2luZG93LmdvID0gd2luZG93LmdvIHx8IHt9O1xuXG5cdC8vIEl0ZXJhdGUgcGFja2FnZSBuYW1lc1xuXHRPYmplY3Qua2V5cyhiaW5kaW5nc01hcCkuZm9yRWFjaCgocGFja2FnZU5hbWUpID0+IHtcblxuXHRcdC8vIENyZWF0ZSBpbm5lciBtYXAgaWYgaXQgZG9lc24ndCBleGlzdFxuXHRcdHdpbmRvdy5nb1twYWNrYWdlTmFtZV0gPSB3aW5kb3cuZ29bcGFja2FnZU5hbWVdIHx8IHt9O1xuXG5cdFx0Ly8gSXRlcmF0ZSBzdHJ1Y3QgbmFtZXNcblx0XHRPYmplY3Qua2V5cyhiaW5kaW5nc01hcFtwYWNrYWdlTmFtZV0pLmZvckVhY2goKHN0cnVjdE5hbWUpID0+IHtcblxuXHRcdFx0Ly8gQ3JlYXRlIGlubmVyIG1hcCBpZiBpdCBkb2Vzbid0IGV4aXN0XG5cdFx0XHR3aW5kb3cuZ29bcGFja2FnZU5hbWVdW3N0cnVjdE5hbWVdID0gd2luZG93LmdvW3BhY2thZ2VOYW1lXVtzdHJ1Y3ROYW1lXSB8fCB7fTtcblxuXHRcdFx0T2JqZWN0LmtleXMoYmluZGluZ3NNYXBbcGFja2FnZU5hbWVdW3N0cnVjdE5hbWVdKS5mb3JFYWNoKChtZXRob2ROYW1lKSA9PiB7XG5cblx0XHRcdFx0d2luZG93LmdvW3BhY2thZ2VOYW1lXVtzdHJ1Y3ROYW1lXVttZXRob2ROYW1lXSA9IGZ1bmN0aW9uICgpIHtcblxuXHRcdFx0XHRcdC8vIE5vIHRpbWVvdXQgYnkgZGVmYXVsdFxuXHRcdFx0XHRcdGxldCB0aW1lb3V0ID0gMDtcblxuXHRcdFx0XHRcdC8vIEFjdHVhbCBmdW5jdGlvblxuXHRcdFx0XHRcdGZ1bmN0aW9uIGR5bmFtaWMoKSB7XG5cdFx0XHRcdFx0XHRjb25zdCBhcmdzID0gW10uc2xpY2UuY2FsbChhcmd1bWVudHMpO1xuXHRcdFx0XHRcdFx0cmV0dXJuIENhbGwoW3BhY2thZ2VOYW1lLCBzdHJ1Y3ROYW1lLCBtZXRob2ROYW1lXS5qb2luKCcuJyksIGFyZ3MsIHRpbWVvdXQpO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIEFsbG93IHNldHRpbmcgdGltZW91dCB0byBmdW5jdGlvblxuXHRcdFx0XHRcdGR5bmFtaWMuc2V0VGltZW91dCA9IGZ1bmN0aW9uIChuZXdUaW1lb3V0KSB7XG5cdFx0XHRcdFx0XHR0aW1lb3V0ID0gbmV3VGltZW91dDtcblx0XHRcdFx0XHR9O1xuXG5cdFx0XHRcdFx0Ly8gQWxsb3cgZ2V0dGluZyB0aW1lb3V0IHRvIGZ1bmN0aW9uXG5cdFx0XHRcdFx0ZHluYW1pYy5nZXRUaW1lb3V0ID0gZnVuY3Rpb24gKCkge1xuXHRcdFx0XHRcdFx0cmV0dXJuIHRpbWVvdXQ7XG5cdFx0XHRcdFx0fTtcblxuXHRcdFx0XHRcdHJldHVybiBkeW5hbWljO1xuXHRcdFx0XHR9KCk7XG5cdFx0XHR9KTtcblx0XHR9KTtcblx0fSk7XG59XG4iLCAiLypcbiBfXHQgICBfX1x0ICBfIF9fXG58IHxcdCAvIC9fX18gXyhfKSAvX19fX1xufCB8IC98IC8gLyBfXyBgLyAvIC8gX19fL1xufCB8LyB8LyAvIC9fLyAvIC8gKF9fICApXG58X18vfF9fL1xcX18sXy9fL18vX19fXy9cblRoZSBlbGVjdHJvbiBhbHRlcm5hdGl2ZSBmb3IgR29cbihjKSBMZWEgQW50aG9ueSAyMDE5LXByZXNlbnRcbiovXG5cbi8qIGpzaGludCBlc3ZlcnNpb246IDkgKi9cblxuXG5pbXBvcnQge0NhbGx9IGZyb20gXCIuL2NhbGxzXCI7XG5cbmV4cG9ydCBmdW5jdGlvbiBXaW5kb3dSZWxvYWQoKSB7XG4gICAgd2luZG93LmxvY2F0aW9uLnJlbG9hZCgpO1xufVxuXG4vKipcbiAqIFBsYWNlIHRoZSB3aW5kb3cgaW4gdGhlIGNlbnRlciBvZiB0aGUgc2NyZWVuXG4gKlxuICogQGV4cG9ydFxuICovXG5leHBvcnQgZnVuY3Rpb24gV2luZG93Q2VudGVyKCkge1xuICAgIHdpbmRvdy5XYWlsc0ludm9rZSgnV2MnKTtcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSB3aW5kb3cgdGl0bGVcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gdGl0bGVcbiAqIEBleHBvcnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFdpbmRvd1NldFRpdGxlKHRpdGxlKSB7XG4gICAgd2luZG93LldhaWxzSW52b2tlKCdXVCcgKyB0aXRsZSk7XG59XG5cbi8qKlxuICogTWFrZXMgdGhlIHdpbmRvdyBnbyBmdWxsc2NyZWVuXG4gKlxuICogQGV4cG9ydFxuICovXG5leHBvcnQgZnVuY3Rpb24gV2luZG93RnVsbHNjcmVlbigpIHtcbiAgICB3aW5kb3cuV2FpbHNJbnZva2UoJ1dGJyk7XG59XG5cbi8qKlxuICogUmV2ZXJ0cyB0aGUgd2luZG93IGZyb20gZnVsbHNjcmVlblxuICpcbiAqIEBleHBvcnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFdpbmRvd1VuRnVsbHNjcmVlbigpIHtcbiAgICB3aW5kb3cuV2FpbHNJbnZva2UoJ1dmJyk7XG59XG5cbi8qKlxuICogU2V0IHRoZSBTaXplIG9mIHRoZSB3aW5kb3dcbiAqXG4gKiBAZXhwb3J0XG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGhcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFdpbmRvd1NldFNpemUod2lkdGgsIGhlaWdodCkge1xuICAgIHdpbmRvdy5XYWlsc0ludm9rZSgnV3M6JyArIHdpZHRoICsgJzonICsgaGVpZ2h0KTtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIFNpemUgb2YgdGhlIHdpbmRvd1xuICpcbiAqIEBleHBvcnRcbiAqIEByZXR1cm4ge1Byb21pc2U8e3c6IG51bWJlciwgaDogbnVtYmVyfT59IFRoZSBzaXplIG9mIHRoZSB3aW5kb3dcblxuICovXG5leHBvcnQgZnVuY3Rpb24gV2luZG93R2V0U2l6ZSgpIHtcbiAgICByZXR1cm4gQ2FsbChcIjp3YWlsczpXaW5kb3dHZXRTaXplXCIpO1xufVxuXG4vKipcbiAqIFNldCB0aGUgbWF4aW11bSBzaXplIG9mIHRoZSB3aW5kb3dcbiAqXG4gKiBAZXhwb3J0XG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGhcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFdpbmRvd1NldE1heFNpemUod2lkdGgsIGhlaWdodCkge1xuICAgIHdpbmRvdy5XYWlsc0ludm9rZSgnV1o6JyArIHdpZHRoICsgJzonICsgaGVpZ2h0KTtcbn1cblxuLyoqXG4gKiBTZXQgdGhlIG1pbmltdW0gc2l6ZSBvZiB0aGUgd2luZG93XG4gKlxuICogQGV4cG9ydFxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBXaW5kb3dTZXRNaW5TaXplKHdpZHRoLCBoZWlnaHQpIHtcbiAgICB3aW5kb3cuV2FpbHNJbnZva2UoJ1d6OicgKyB3aWR0aCArICc6JyArIGhlaWdodCk7XG59XG5cbi8qKlxuICogU2V0IHRoZSBQb3NpdGlvbiBvZiB0aGUgd2luZG93XG4gKlxuICogQGV4cG9ydFxuICogQHBhcmFtIHtudW1iZXJ9IHhcbiAqIEBwYXJhbSB7bnVtYmVyfSB5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBXaW5kb3dTZXRQb3NpdGlvbih4LCB5KSB7XG4gICAgd2luZG93LldhaWxzSW52b2tlKCdXcDonICsgeCArICc6JyArIHkpO1xufVxuXG4vKipcbiAqIEdldCB0aGUgUG9zaXRpb24gb2YgdGhlIHdpbmRvd1xuICpcbiAqIEBleHBvcnRcbiAqIEByZXR1cm4ge1Byb21pc2U8e3g6IG51bWJlciwgeTogbnVtYmVyfT59IFRoZSBwb3NpdGlvbiBvZiB0aGUgd2luZG93XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBXaW5kb3dHZXRQb3NpdGlvbigpIHtcbiAgICByZXR1cm4gQ2FsbChcIjp3YWlsczpXaW5kb3dHZXRQb3NcIik7XG59XG5cbi8qKlxuICogSGlkZSB0aGUgV2luZG93XG4gKlxuICogQGV4cG9ydFxuICovXG5leHBvcnQgZnVuY3Rpb24gV2luZG93SGlkZSgpIHtcbiAgICB3aW5kb3cuV2FpbHNJbnZva2UoJ1dIJyk7XG59XG5cbi8qKlxuICogU2hvdyB0aGUgV2luZG93XG4gKlxuICogQGV4cG9ydFxuICovXG5leHBvcnQgZnVuY3Rpb24gV2luZG93U2hvdygpIHtcbiAgICB3aW5kb3cuV2FpbHNJbnZva2UoJ1dTJyk7XG59XG5cbi8qKlxuICogTWF4aW1pc2UgdGhlIFdpbmRvd1xuICpcbiAqIEBleHBvcnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFdpbmRvd01heGltaXNlKCkge1xuICAgIHdpbmRvdy5XYWlsc0ludm9rZSgnV00nKTtcbn1cblxuLyoqXG4gKiBVbm1heGltaXNlIHRoZSBXaW5kb3dcbiAqXG4gKiBAZXhwb3J0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBXaW5kb3dVbm1heGltaXNlKCkge1xuICAgIHdpbmRvdy5XYWlsc0ludm9rZSgnV1UnKTtcbn1cblxuLyoqXG4gKiBNaW5pbWlzZSB0aGUgV2luZG93XG4gKlxuICogQGV4cG9ydFxuICovXG5leHBvcnQgZnVuY3Rpb24gV2luZG93TWluaW1pc2UoKSB7XG4gICAgd2luZG93LldhaWxzSW52b2tlKCdXbScpO1xufVxuXG4vKipcbiAqIFVubWluaW1pc2UgdGhlIFdpbmRvd1xuICpcbiAqIEBleHBvcnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFdpbmRvd1VubWluaW1pc2UoKSB7XG4gICAgd2luZG93LldhaWxzSW52b2tlKCdXdScpO1xufVxuXG5cbi8qKlxuICogU2V0cyB0aGUgYmFja2dyb3VuZCBjb2xvdXIgb2YgdGhlIHdpbmRvd1xuICpcbiAqIEBleHBvcnRcbiAqIEBwYXJhbSB7UkdCQX0gUkdCQSBiYWNrZ3JvdW5kIGNvbG91clxuICovXG5leHBvcnQgZnVuY3Rpb24gV2luZG93U2V0UkdCQShSR0JBKSB7XG4gICAgbGV0IHJnYmEgPSBKU09OLnN0cmluZ2lmeShSR0JBKTtcbiAgICB3aW5kb3cuV2FpbHNJbnZva2UoJ1dyOicgKyByZ2JhKTtcbn1cblxuIiwgIi8qKlxuICogQGRlc2NyaXB0aW9uOiBVc2UgdGhlIHN5c3RlbSBkZWZhdWx0IGJyb3dzZXIgdG8gb3BlbiB0aGUgdXJsXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIFxuICogQHJldHVybiB7dm9pZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEJyb3dzZXJPcGVuVVJMKHVybCkge1xuICB3aW5kb3cuV2FpbHNJbnZva2UoJ0JPOicgKyB1cmwpO1xufSIsICIvKlxuIF9cdCAgIF9fXHQgIF8gX19cbnwgfFx0IC8gL19fXyBfKF8pIC9fX19fXG58IHwgL3wgLyAvIF9fIGAvIC8gLyBfX18vXG58IHwvIHwvIC8gL18vIC8gLyAoX18gIClcbnxfXy98X18vXFxfXyxfL18vXy9fX19fL1xuVGhlIGVsZWN0cm9uIGFsdGVybmF0aXZlIGZvciBHb1xuKGMpIExlYSBBbnRob255IDIwMTktcHJlc2VudFxuKi9cbi8qIGpzaGludCBlc3ZlcnNpb246IDkgKi9cbmltcG9ydCAqIGFzIExvZyBmcm9tICcuL2xvZyc7XG5pbXBvcnQge2V2ZW50TGlzdGVuZXJzLCBFdmVudHNFbWl0LCBFdmVudHNOb3RpZnksIEV2ZW50c09mZiwgRXZlbnRzT24sIEV2ZW50c09uY2UsIEV2ZW50c09uTXVsdGlwbGV9IGZyb20gJy4vZXZlbnRzJztcbmltcG9ydCB7Q2FsbGJhY2ssIGNhbGxiYWNrc30gZnJvbSAnLi9jYWxscyc7XG5pbXBvcnQge1NldEJpbmRpbmdzfSBmcm9tIFwiLi9iaW5kaW5nc1wiO1xuaW1wb3J0ICogYXMgV2luZG93IGZyb20gXCIuL3dpbmRvd1wiO1xuaW1wb3J0ICogYXMgQnJvd3NlciBmcm9tIFwiLi9icm93c2VyXCI7XG5cblxuZXhwb3J0IGZ1bmN0aW9uIFF1aXQoKSB7XG4gICAgd2luZG93LldhaWxzSW52b2tlKCdRJyk7XG59XG5cbi8vIFRoZSBKUyBydW50aW1lXG53aW5kb3cucnVudGltZSA9IHtcbiAgICAuLi5Mb2csXG4gICAgLi4uV2luZG93LFxuICAgIC4uLkJyb3dzZXIsXG4gICAgRXZlbnRzT24sXG4gICAgRXZlbnRzT25jZSxcbiAgICBFdmVudHNPbk11bHRpcGxlLFxuICAgIEV2ZW50c0VtaXQsXG4gICAgRXZlbnRzT2ZmLFxuICAgIFF1aXRcbn07XG5cbi8vIEludGVybmFsIHdhaWxzIGVuZHBvaW50c1xud2luZG93LndhaWxzID0ge1xuICAgIENhbGxiYWNrLFxuICAgIEV2ZW50c05vdGlmeSxcbiAgICBTZXRCaW5kaW5ncyxcbiAgICBldmVudExpc3RlbmVycyxcbiAgICBjYWxsYmFja3MsXG4gICAgZmxhZ3M6IHtcbiAgICAgICAgZGlzYWJsZVNjcm9sbGJhckRyYWc6IGZhbHNlLFxuICAgICAgICBkaXNhYmxlV2FpbHNEZWZhdWx0Q29udGV4dE1lbnU6IGZhbHNlLFxuICAgIH1cbn07XG5cbi8vIFNldCB0aGUgYmluZGluZ3NcbndpbmRvdy53YWlscy5TZXRCaW5kaW5ncyh3aW5kb3cud2FpbHNiaW5kaW5ncyk7XG5kZWxldGUgd2luZG93LndhaWxzLlNldEJpbmRpbmdzO1xuXG4vLyBUaGlzIGlzIGV2YWx1YXRlZCBhdCBidWlsZCB0aW1lIGluIHBhY2thZ2UuanNvblxuLy8gY29uc3QgZGV2ID0gMDtcbi8vIGNvbnN0IHByb2R1Y3Rpb24gPSAxO1xuaWYgKEVOViA9PT0gMCkge1xuICAgIGRlbGV0ZSB3aW5kb3cud2FpbHNiaW5kaW5ncztcbn1cblxuLy8gU2V0dXAgZHJhZyBoYW5kbGVyXG4vLyBCYXNlZCBvbiBjb2RlIGZyb206IGh0dHBzOi8vZ2l0aHViLmNvbS9wYXRyMG51cy9EZXNrR2FwXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vkb3duJywgKGUpID0+IHtcbiAgICBsZXQgY3VycmVudEVsZW1lbnQgPSBlLnRhcmdldDtcbiAgICB3aGlsZSAoY3VycmVudEVsZW1lbnQgIT0gbnVsbCkge1xuICAgICAgICBpZiAoY3VycmVudEVsZW1lbnQuaGFzQXR0cmlidXRlKCdkYXRhLXdhaWxzLW5vLWRyYWcnKSkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH0gZWxzZSBpZiAoY3VycmVudEVsZW1lbnQuaGFzQXR0cmlidXRlKCdkYXRhLXdhaWxzLWRyYWcnKSkge1xuICAgICAgICAgICAgaWYgKHdpbmRvdy53YWlscy5mbGFncy5kaXNhYmxlU2Nyb2xsYmFyRHJhZykge1xuICAgICAgICAgICAgICAgIC8vIFRoaXMgY2hlY2tzIGZvciBjbGlja3Mgb24gdGhlIHNjcm9sbCBiYXJcbiAgICAgICAgICAgICAgICBpZiAoZS5vZmZzZXRYID4gZS50YXJnZXQuY2xpZW50V2lkdGggfHwgZS5vZmZzZXRZID4gZS50YXJnZXQuY2xpZW50SGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHdpbmRvdy5XYWlsc0ludm9rZShcImRyYWdcIik7XG4gICAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBjdXJyZW50RWxlbWVudCA9IGN1cnJlbnRFbGVtZW50LnBhcmVudEVsZW1lbnQ7XG4gICAgfVxufSk7XG5cbi8vIFNldHVwIGNvbnRleHQgbWVudSBob29rXG53aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignY29udGV4dG1lbnUnLCBmdW5jdGlvbiAoZSkge1xuICAgIGlmICh3aW5kb3cud2FpbHMuZmxhZ3MuZGlzYWJsZVdhaWxzRGVmYXVsdENvbnRleHRNZW51KSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICB9XG59KTsiXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBa0JBLDBCQUF3QixPQUFPLFNBQVM7QUFJdkMsV0FBTyxZQUFZLE1BQU0sUUFBUTtBQUFBO0FBUzNCLG9CQUFrQixTQUFTO0FBQ2pDLG1CQUFlLEtBQUs7QUFBQTtBQVNkLG9CQUFrQixTQUFTO0FBQ2pDLG1CQUFlLEtBQUs7QUFBQTtBQVNkLG9CQUFrQixTQUFTO0FBQ2pDLG1CQUFlLEtBQUs7QUFBQTtBQVNkLG1CQUFpQixTQUFTO0FBQ2hDLG1CQUFlLEtBQUs7QUFBQTtBQVNkLHNCQUFvQixTQUFTO0FBQ25DLG1CQUFlLEtBQUs7QUFBQTtBQVNkLG9CQUFrQixTQUFTO0FBQ2pDLG1CQUFlLEtBQUs7QUFBQTtBQVNkLG9CQUFrQixTQUFTO0FBQ2pDLG1CQUFlLEtBQUs7QUFBQTtBQVNkLHVCQUFxQixVQUFVO0FBQ3JDLG1CQUFlLEtBQUs7QUFBQTtBQUlkLE1BQU0sV0FBVztBQUFBLElBQ3ZCLE9BQU87QUFBQSxJQUNQLE9BQU87QUFBQSxJQUNQLE1BQU07QUFBQSxJQUNOLFNBQVM7QUFBQSxJQUNULE9BQU87QUFBQTs7O0FDN0ZSLHVCQUFlO0FBQUEsSUFPWCxZQUFZLFVBQVUsY0FBYztBQUVoQyxxQkFBZSxnQkFBZ0I7QUFHL0IsV0FBSyxXQUFXLENBQUMsU0FBUztBQUN0QixpQkFBUyxNQUFNLE1BQU07QUFFckIsWUFBSSxpQkFBaUIsSUFBSTtBQUNyQixpQkFBTztBQUFBO0FBR1gsd0JBQWdCO0FBQ2hCLGVBQU8saUJBQWlCO0FBQUE7QUFBQTtBQUFBO0FBSzdCLE1BQU0saUJBQWlCO0FBVXZCLDRCQUEwQixXQUFXLFVBQVUsY0FBYztBQUNoRSxtQkFBZSxhQUFhLGVBQWUsY0FBYztBQUN6RCxVQUFNLGVBQWUsSUFBSSxTQUFTLFVBQVU7QUFDNUMsbUJBQWUsV0FBVyxLQUFLO0FBQUE7QUFVNUIsb0JBQWtCLFdBQVcsVUFBVTtBQUMxQyxxQkFBaUIsV0FBVyxVQUFVO0FBQUE7QUFVbkMsc0JBQW9CLFdBQVcsVUFBVTtBQUM1QyxxQkFBaUIsV0FBVyxVQUFVO0FBQUE7QUFHMUMsMkJBQXlCLFdBQVc7QUFHaEMsUUFBSSxZQUFZLFVBQVU7QUFHMUIsUUFBSSxlQUFlLFlBQVk7QUFHM0IsWUFBTSx1QkFBdUIsZUFBZSxXQUFXO0FBR3ZELGVBQVMsUUFBUSxHQUFHLFFBQVEsZUFBZSxXQUFXLFFBQVEsU0FBUyxHQUFHO0FBR3RFLGNBQU0sV0FBVyxlQUFlLFdBQVc7QUFFM0MsWUFBSSxPQUFPLFVBQVU7QUFHckIsY0FBTSxVQUFVLFNBQVMsU0FBUztBQUNsQyxZQUFJLFNBQVM7QUFFVCwrQkFBcUIsT0FBTyxPQUFPO0FBQUE7QUFBQTtBQUszQyxxQkFBZSxhQUFhO0FBQUE7QUFBQTtBQVc3Qix3QkFBc0IsZUFBZTtBQUV4QyxRQUFJO0FBQ0osUUFBSTtBQUNBLGdCQUFVLEtBQUssTUFBTTtBQUFBLGFBQ2hCLEdBQVA7QUFDRSxZQUFNLFFBQVEsb0NBQW9DO0FBQ2xELFlBQU0sSUFBSSxNQUFNO0FBQUE7QUFFcEIsb0JBQWdCO0FBQUE7QUFTYixzQkFBb0IsV0FBVztBQUVsQyxVQUFNLFVBQVU7QUFBQSxNQUNaLE1BQU07QUFBQSxNQUNOLE1BQU0sR0FBRyxNQUFNLE1BQU0sV0FBVyxNQUFNO0FBQUE7QUFJMUMsb0JBQWdCO0FBR2hCLFdBQU8sWUFBWSxPQUFPLEtBQUssVUFBVTtBQUFBO0FBR3RDLHFCQUFtQixXQUFXO0FBRWpDLG1CQUFlLE9BQU87QUFHdEIsV0FBTyxZQUFZLE9BQU87QUFBQTs7O0FDbEp2QixNQUFNLFlBQVk7QUFPekIsMEJBQXdCO0FBQ3ZCLFFBQUksUUFBUSxJQUFJLFlBQVk7QUFDNUIsV0FBTyxPQUFPLE9BQU8sZ0JBQWdCLE9BQU87QUFBQTtBQVM3Qyx5QkFBdUI7QUFDdEIsV0FBTyxLQUFLLFdBQVc7QUFBQTtBQUl4QixNQUFJO0FBQ0osTUFBSSxPQUFPLFFBQVE7QUFDbEIsaUJBQWE7QUFBQSxTQUNQO0FBQ04saUJBQWE7QUFBQTtBQWtCUCxnQkFBYyxNQUFNLE1BQU0sU0FBUztBQUd6QyxRQUFJLFdBQVcsTUFBTTtBQUNwQixnQkFBVTtBQUFBO0FBSVgsV0FBTyxJQUFJLFFBQVEsU0FBVSxTQUFTLFFBQVE7QUFHN0MsVUFBSTtBQUNKLFNBQUc7QUFDRixxQkFBYSxPQUFPLE1BQU07QUFBQSxlQUNsQixVQUFVO0FBRW5CLFVBQUk7QUFFSixVQUFJLFVBQVUsR0FBRztBQUNoQix3QkFBZ0IsV0FBVyxXQUFZO0FBQ3RDLGlCQUFPLE1BQU0sYUFBYSxPQUFPLDZCQUE2QjtBQUFBLFdBQzVEO0FBQUE7QUFJSixnQkFBVSxjQUFjO0FBQUEsUUFDdkI7QUFBQSxRQUNBO0FBQUEsUUFDQTtBQUFBO0FBR0QsVUFBSTtBQUNILGNBQU0sVUFBVTtBQUFBLFVBQ2Y7QUFBQSxVQUNBO0FBQUEsVUFDQTtBQUFBO0FBSUQsZUFBTyxZQUFZLE1BQU0sS0FBSyxVQUFVO0FBQUEsZUFDaEMsR0FBUDtBQUVELGdCQUFRLE1BQU07QUFBQTtBQUFBO0FBQUE7QUFjVixvQkFBa0IsaUJBQWlCO0FBS3pDLFFBQUk7QUFDSixRQUFJO0FBQ0gsZ0JBQVUsS0FBSyxNQUFNO0FBQUEsYUFDYixHQUFQO0FBQ0QsWUFBTSxRQUFRLG9DQUFvQyxFQUFFLHFCQUFxQjtBQUN6RSxZQUFNLFNBQVM7QUFDZixZQUFNLElBQUksTUFBTTtBQUFBO0FBRWpCLFFBQUksYUFBYSxRQUFRO0FBQ3pCLFFBQUksZUFBZSxVQUFVO0FBQzdCLFFBQUksQ0FBQyxjQUFjO0FBQ2xCLFlBQU0sUUFBUSxhQUFhO0FBQzNCLGNBQVEsTUFBTTtBQUNkLFlBQU0sSUFBSSxNQUFNO0FBQUE7QUFFakIsaUJBQWEsYUFBYTtBQUUxQixXQUFPLFVBQVU7QUFFakIsUUFBSSxRQUFRLE9BQU87QUFDbEIsbUJBQWEsT0FBTyxRQUFRO0FBQUEsV0FDdEI7QUFDTixtQkFBYSxRQUFRLFFBQVE7QUFBQTtBQUFBOzs7QUM3SC9CLFNBQU8sS0FBSztBQUVMLHVCQUFxQixhQUFhO0FBQ3hDLFFBQUk7QUFDSCxvQkFBYyxLQUFLLE1BQU07QUFBQSxhQUNqQixHQUFQO0FBQ0QsY0FBUSxNQUFNO0FBQUE7QUFJZixXQUFPLEtBQUssT0FBTyxNQUFNO0FBR3pCLFdBQU8sS0FBSyxhQUFhLFFBQVEsQ0FBQyxnQkFBZ0I7QUFHakQsYUFBTyxHQUFHLGVBQWUsT0FBTyxHQUFHLGdCQUFnQjtBQUduRCxhQUFPLEtBQUssWUFBWSxjQUFjLFFBQVEsQ0FBQyxlQUFlO0FBRzdELGVBQU8sR0FBRyxhQUFhLGNBQWMsT0FBTyxHQUFHLGFBQWEsZUFBZTtBQUUzRSxlQUFPLEtBQUssWUFBWSxhQUFhLGFBQWEsUUFBUSxDQUFDLGVBQWU7QUFFekUsaUJBQU8sR0FBRyxhQUFhLFlBQVksY0FBYyxXQUFZO0FBRzVELGdCQUFJLFVBQVU7QUFHZCwrQkFBbUI7QUFDbEIsb0JBQU0sT0FBTyxHQUFHLE1BQU0sS0FBSztBQUMzQixxQkFBTyxLQUFLLENBQUMsYUFBYSxZQUFZLFlBQVksS0FBSyxNQUFNLE1BQU07QUFBQTtBQUlwRSxvQkFBUSxhQUFhLFNBQVUsWUFBWTtBQUMxQyx3QkFBVTtBQUFBO0FBSVgsb0JBQVEsYUFBYSxXQUFZO0FBQ2hDLHFCQUFPO0FBQUE7QUFHUixtQkFBTztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7OztBQzdEWjtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFlTywwQkFBd0I7QUFDM0IsV0FBTyxTQUFTO0FBQUE7QUFRYiwwQkFBd0I7QUFDM0IsV0FBTyxZQUFZO0FBQUE7QUFTaEIsMEJBQXdCLE9BQU87QUFDbEMsV0FBTyxZQUFZLE9BQU87QUFBQTtBQVF2Qiw4QkFBNEI7QUFDL0IsV0FBTyxZQUFZO0FBQUE7QUFRaEIsZ0NBQThCO0FBQ2pDLFdBQU8sWUFBWTtBQUFBO0FBVWhCLHlCQUF1QixPQUFPLFFBQVE7QUFDekMsV0FBTyxZQUFZLFFBQVEsUUFBUSxNQUFNO0FBQUE7QUFVdEMsMkJBQXlCO0FBQzVCLFdBQU8sS0FBSztBQUFBO0FBVVQsNEJBQTBCLE9BQU8sUUFBUTtBQUM1QyxXQUFPLFlBQVksUUFBUSxRQUFRLE1BQU07QUFBQTtBQVV0Qyw0QkFBMEIsT0FBTyxRQUFRO0FBQzVDLFdBQU8sWUFBWSxRQUFRLFFBQVEsTUFBTTtBQUFBO0FBVXRDLDZCQUEyQixHQUFHLEdBQUc7QUFDcEMsV0FBTyxZQUFZLFFBQVEsSUFBSSxNQUFNO0FBQUE7QUFTbEMsK0JBQTZCO0FBQ2hDLFdBQU8sS0FBSztBQUFBO0FBUVQsd0JBQXNCO0FBQ3pCLFdBQU8sWUFBWTtBQUFBO0FBUWhCLHdCQUFzQjtBQUN6QixXQUFPLFlBQVk7QUFBQTtBQVFoQiw0QkFBMEI7QUFDN0IsV0FBTyxZQUFZO0FBQUE7QUFRaEIsOEJBQTRCO0FBQy9CLFdBQU8sWUFBWTtBQUFBO0FBUWhCLDRCQUEwQjtBQUM3QixXQUFPLFlBQVk7QUFBQTtBQVFoQiw4QkFBNEI7QUFDL0IsV0FBTyxZQUFZO0FBQUE7QUFVaEIseUJBQXVCLE1BQU07QUFDaEMsUUFBSSxPQUFPLEtBQUssVUFBVTtBQUMxQixXQUFPLFlBQVksUUFBUTtBQUFBOzs7QUN4TC9CO0FBQUE7QUFBQTtBQUFBO0FBS08sMEJBQXdCLEtBQUs7QUFDbEMsV0FBTyxZQUFZLFFBQVE7QUFBQTs7O0FDWXRCLGtCQUFnQjtBQUNuQixXQUFPLFlBQVk7QUFBQTtBQUl2QixTQUFPLFVBQVU7QUFBQSxPQUNWO0FBQUEsT0FDQTtBQUFBLE9BQ0E7QUFBQSxJQUNIO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQTtBQUlKLFNBQU8sUUFBUTtBQUFBLElBQ1g7QUFBQSxJQUNBO0FBQUEsSUFDQTtBQUFBLElBQ0E7QUFBQSxJQUNBO0FBQUEsSUFDQSxPQUFPO0FBQUEsTUFDSCxzQkFBc0I7QUFBQSxNQUN0QixnQ0FBZ0M7QUFBQTtBQUFBO0FBS3hDLFNBQU8sTUFBTSxZQUFZLE9BQU87QUFDaEMsU0FBTyxPQUFPLE1BQU07QUFLcEIsTUFBSSxNQUFXO0FBQ1gsV0FBTyxPQUFPO0FBQUE7QUFLbEIsU0FBTyxpQkFBaUIsYUFBYSxDQUFDLE1BQU07QUFDeEMsUUFBSSxpQkFBaUIsRUFBRTtBQUN2QixXQUFPLGtCQUFrQixNQUFNO0FBQzNCLFVBQUksZUFBZSxhQUFhLHVCQUF1QjtBQUNuRDtBQUFBLGlCQUNPLGVBQWUsYUFBYSxvQkFBb0I7QUFDdkQsWUFBSSxPQUFPLE1BQU0sTUFBTSxzQkFBc0I7QUFFekMsY0FBSSxFQUFFLFVBQVUsRUFBRSxPQUFPLGVBQWUsRUFBRSxVQUFVLEVBQUUsT0FBTyxjQUFjO0FBQ3ZFO0FBQUE7QUFBQTtBQUdSLGVBQU8sWUFBWTtBQUNuQixVQUFFO0FBQ0Y7QUFBQTtBQUVKLHVCQUFpQixlQUFlO0FBQUE7QUFBQTtBQUt4QyxTQUFPLGlCQUFpQixlQUFlLFNBQVUsR0FBRztBQUNoRCxRQUFJLE9BQU8sTUFBTSxNQUFNLGdDQUFnQztBQUNuRCxRQUFFO0FBQUE7QUFBQTsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/v2/internal/frontend/runtime/runtime_prod_desktop.go b/v2/internal/frontend/runtime/runtime_prod_desktop.go new file mode 100644 index 000000000..feba8a847 --- /dev/null +++ b/v2/internal/frontend/runtime/runtime_prod_desktop.go @@ -0,0 +1,8 @@ +//go:build production && desktop + +package runtime + +import _ "embed" + +//go:embed runtime_prod_desktop.js +var RuntimeDesktopJS []byte diff --git a/v2/internal/frontend/runtime/runtime_prod_desktop.js b/v2/internal/frontend/runtime/runtime_prod_desktop.js new file mode 100644 index 000000000..75a7b6924 --- /dev/null +++ b/v2/internal/frontend/runtime/runtime_prod_desktop.js @@ -0,0 +1 @@ +(()=>{var x=Object.defineProperty;var h=n=>x(n,"__esModule",{value:!0});var u=(n,o)=>{h(n);for(var e in o)x(n,e,{get:o[e],enumerable:!0})};var W={};u(W,{LogDebug:()=>C,LogError:()=>J,LogFatal:()=>F,LogInfo:()=>B,LogLevel:()=>U,LogPrint:()=>R,LogTrace:()=>D,LogWarning:()=>T,SetLogLevel:()=>G});function l(n,o){window.WailsInvoke("L"+n+o)}function D(n){l("T",n)}function R(n){l("P",n)}function C(n){l("D",n)}function B(n){l("I",n)}function T(n){l("W",n)}function J(n){l("E",n)}function F(n){l("F",n)}function G(n){l("S",n)}var U={TRACE:1,DEBUG:2,INFO:3,WARNING:4,ERROR:5};var E=class{constructor(o,e){e=e||-1,this.Callback=t=>(o.apply(null,t),e===-1?!1:(e-=1,e===0))}},s={};function d(n,o,e){s[n]=s[n]||[];let t=new E(o,e);s[n].push(t)}function I(n,o){d(n,o,-1)}function k(n,o){d(n,o,1)}function S(n){let o=n.name;if(s[o]){let e=s[o].slice();for(let t=0;t0&&(w=setTimeout(function(){r(Error("Call to "+n+" timed out. Request ID: "+i))},e)),a[i]={timeoutHandle:w,reject:r,resolve:t};try{let f={name:n,args:o,callbackID:i};window.WailsInvoke("C"+JSON.stringify(f))}catch(f){console.error(f)}})}function L(n){var o;try{o=JSON.parse(n)}catch(r){let i=`Invalid JSON passed to callback: ${r.message}. Message: ${n}`;throw wails.LogDebug(i),new Error(i)}var e=o.callbackid,t=a[e];if(!t){let r=`Callback '${e}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(t.timeoutHandle),delete a[e],o.error?t.reject(o.error):t.resolve(o.result)}window.go={};function O(n){try{n=JSON.parse(n)}catch(o){console.error(o)}window.go=window.go||{},Object.keys(n).forEach(o=>{window.go[o]=window.go[o]||{},Object.keys(n[o]).forEach(e=>{window.go[o][e]=window.go[o][e]||{},Object.keys(n[o][e]).forEach(t=>{window.go[o][e][t]=function(){let r=0;function i(){let w=[].slice.call(arguments);return c([o,e,t].join("."),w,r)}return i.setTimeout=function(w){r=w},i.getTimeout=function(){return r},i}()})})})}var v={};u(v,{WindowCenter:()=>M,WindowFullscreen:()=>j,WindowGetPosition:()=>Y,WindowGetSize:()=>V,WindowHide:()=>Z,WindowMaximise:()=>_,WindowMinimise:()=>on,WindowReload:()=>H,WindowSetMaxSize:()=>X,WindowSetMinSize:()=>q,WindowSetPosition:()=>N,WindowSetRGBA:()=>tn,WindowSetSize:()=>Q,WindowSetTitle:()=>P,WindowShow:()=>K,WindowUnFullscreen:()=>$,WindowUnmaximise:()=>nn,WindowUnminimise:()=>en});function H(){window.location.reload()}function M(){window.WailsInvoke("Wc")}function P(n){window.WailsInvoke("WT"+n)}function j(){window.WailsInvoke("WF")}function $(){window.WailsInvoke("Wf")}function Q(n,o){window.WailsInvoke("Ws:"+n+":"+o)}function V(){return c(":wails:WindowGetSize")}function X(n,o){window.WailsInvoke("WZ:"+n+":"+o)}function q(n,o){window.WailsInvoke("Wz:"+n+":"+o)}function N(n,o){window.WailsInvoke("Wp:"+n+":"+o)}function Y(){return c(":wails:WindowGetPos")}function Z(){window.WailsInvoke("WH")}function K(){window.WailsInvoke("WS")}function _(){window.WailsInvoke("WM")}function nn(){window.WailsInvoke("WU")}function on(){window.WailsInvoke("Wm")}function en(){window.WailsInvoke("Wu")}function tn(n){let o=JSON.stringify(n);window.WailsInvoke("Wr:"+o)}var g={};u(g,{BrowserOpenURL:()=>rn});function rn(n){window.WailsInvoke("BO:"+n)}function sn(){window.WailsInvoke("Q")}window.runtime={...W,...v,...g,EventsOn:I,EventsOnce:k,EventsOnMultiple:d,EventsEmit:b,EventsOff:y,Quit:sn};window.wails={Callback:L,EventsNotify:m,SetBindings:O,eventListeners:s,callbacks:a,flags:{disableScrollbarDrag:!1,disableWailsDefaultContextMenu:!1}};window.wails.SetBindings(window.wailsbindings);delete window.wails.SetBindings;window.addEventListener("mousedown",n=>{let o=n.target;for(;o!=null&&!o.hasAttribute("data-wails-no-drag");){if(o.hasAttribute("data-wails-drag")){if(window.wails.flags.disableScrollbarDrag&&(n.offsetX>n.target.clientWidth||n.offsetY>n.target.clientHeight))break;window.WailsInvoke("drag"),n.preventDefault();break}o=o.parentElement}});window.addEventListener("contextmenu",function(n){window.wails.flags.disableWailsDefaultContextMenu&&n.preventDefault()});})(); diff --git a/v2/internal/frontend/runtime/wrapper/README.md b/v2/internal/frontend/runtime/wrapper/README.md new file mode 100644 index 000000000..a743cffbb --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/README.md @@ -0,0 +1,4 @@ +# Wails Runtime + +This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be +installed as part of a [Wails](https://wails.app) project, not a standalone module. diff --git a/v2/internal/frontend/runtime/wrapper/browser.js b/v2/internal/frontend/runtime/wrapper/browser.js new file mode 100644 index 000000000..f1854820b --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/browser.js @@ -0,0 +1,8 @@ +/** + * @description: Use the system default browser to open the url + * @param {string} url + * @return {void} + */ +export function BrowserOpenURL(url) { + window.runtime.BrowserOpenURL(url); +} \ No newline at end of file diff --git a/v2/internal/frontend/runtime/wrapper/events.js b/v2/internal/frontend/runtime/wrapper/events.js new file mode 100644 index 000000000..623dad51a --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/events.js @@ -0,0 +1,58 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 9 */ + + +/** + * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + * @param {number} maxCallbacks + */ +export function EventsOnMultiple(eventName, callback, maxCallbacks) { + window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks); +} + +/** + * Registers an event listener that will be invoked every time the event is emitted + * + * @export + * @param {string} eventName + * @param {function} callback + */ +export function EventsOn(eventName, callback) { + OnMultiple(eventName, callback, -1); +} + +/** + * Registers an event listener that will be invoked once then destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + */ +export function EventsOnce(eventName, callback) { + OnMultiple(eventName, callback, 1); +} + + +/** + * Emit an event with the given name and data + * + * @export + * @param {string} eventName + */ +export function EventsEmit(eventName) { + let args = [eventName].slice.call(arguments); + return window.runtime.EventsEmit.apply(null, args); +} diff --git a/v2/internal/frontend/runtime/wrapper/log.js b/v2/internal/frontend/runtime/wrapper/log.js new file mode 100644 index 000000000..977abf5f4 --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/log.js @@ -0,0 +1,72 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + + +/** + * Log the given trace message with the backend + * + * @export + * @param {string} message + */ +export function LogTrace(message) { + window.runtime.LogTrace(message); +} + +/** + * Log the given debug message with the backend + * + * @export + * @param {string} message + */ +export function LogDebug(message) { + window.runtime.LogDebug(message); +} + +/** + * Log the given info message with the backend + * + * @export + * @param {string} message + */ +export function LogInfo(message) { + window.runtime.LogInfo(message); +} + +/** + * Log the given warning message with the backend + * + * @export + * @param {string} message + */ +export function LogWarning(message) { + window.runtime.LogWarning(message); +} + +/** + * Log the given error message with the backend + * + * @export + * @param {string} message + */ +export function LogError(message) { + window.runtime.LogError(message); +} + +/** + * Log the given fatal message with the backend + * + * @export + * @param {string} message + */ +export function LogFatal(message) { + window.runtime.LogFatal(message); +} diff --git a/v2/internal/frontend/runtime/wrapper/main.js b/v2/internal/frontend/runtime/wrapper/main.js new file mode 100644 index 000000000..8be95ac4f --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/main.js @@ -0,0 +1,28 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 9 */ + +import * as Log from "./log"; +import * as Events from './events'; +import * as Window from './window'; +import * as Browser from './browser'; + +export function Quit() { + window.runtime.Quit(); +} + + +export default { + ...Log, + ...Events, + ...Window, + ...Browser, + Quit +}; \ No newline at end of file diff --git a/v2/internal/frontend/runtime/wrapper/package-lock.json b/v2/internal/frontend/runtime/wrapper/package-lock.json new file mode 100644 index 000000000..058e19cf3 --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "@wailsapp/runtime", + "version": "2.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@wailsapp/runtime", + "version": "2.0.0", + "license": "MIT" + } + } +} diff --git a/v2/internal/frontend/runtime/wrapper/package.json b/v2/internal/frontend/runtime/wrapper/package.json new file mode 100644 index 000000000..1e7c8a5d7 --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/package.json @@ -0,0 +1,24 @@ +{ + "name": "@wailsapp/runtime", + "version": "2.0.0", + "description": "Wails Javascript runtime library", + "main": "runtime.js", + "types": "runtime.d.ts", + "scripts": { + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wailsapp/wails.git" + }, + "keywords": [ + "Wails", + "Javascript", + "Go" + ], + "author": "Lea Anthony ", + "license": "MIT", + "bugs": { + "url": "https://github.com/wailsapp/wails/issues" + }, + "homepage": "https://github.com/wailsapp/wails#readme" +} diff --git a/v2/internal/frontend/runtime/wrapper/runtime.d.ts b/v2/internal/frontend/runtime/wrapper/runtime.d.ts new file mode 100644 index 000000000..233a67a96 --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/runtime.d.ts @@ -0,0 +1,87 @@ +interface Position { + x: number; + y: number; +} + +interface Size { + w: number; + h: number; +} + +interface RGBA { + r: number; + g: number; + b: number; + a: number; +} + + +interface runtime { + EventsEmit(eventName: string, data?: any): void; + + EventsOn(eventName: string, callback: (data?: any) => void): void; + + EventsOnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void; + + EventsOnce(eventName: string, callback: (data?: any) => void): void; + + LogTrace(message: string): void; + + LogDebug(message: string): void; + + LogError(message: string): void; + + LogFatal(message: string): void; + + LogInfo(message: string): void; + + LogWarning(message: string): void; + + WindowReload(): void; + + WindowCenter(): void; + + WindowSetTitle(title: string): void; + + WindowFullscreen(): void; + + WindowUnFullscreen(): void; + + WindowSetSize(width: number, height: number): Promise; + + WindowGetSize(): Promise; + + WindowSetMaxSize(width: number, height: number): void; + + WindowSetMinSize(width: number, height: number): void; + + WindowSetPosition(x: number, y: number): void; + + WindowGetPosition(): Promise; + + WindowHide(): void; + + WindowShow(): void; + + WindowMaximise(): void; + + WindowUnmaximise(): void; + + WindowMinimise(): void; + + WindowUnminimise(): void; + + WindowSetRGBA(rgba: RGBA): void; + + BrowserOpenURL(url: string): void; + + Quit(): void; +} + +declare global { + interface Window { + runtime: runtime; + } +} + +export { }; diff --git a/v2/internal/frontend/runtime/wrapper/runtime.js b/v2/internal/frontend/runtime/wrapper/runtime.js new file mode 100644 index 000000000..8a2b098a7 --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/runtime.js @@ -0,0 +1 @@ +(()=>{var d=Object.defineProperty;var m=n=>d(n,"__esModule",{value:!0});var t=(n,i)=>{m(n);for(var o in i)d(n,o,{get:i[o],enumerable:!0})};var e={};t(e,{LogDebug:()=>c,LogError:()=>x,LogFatal:()=>s,LogInfo:()=>W,LogTrace:()=>p,LogWarning:()=>f});function p(n){window.runtime.LogTrace(n)}function c(n){window.runtime.LogDebug(n)}function W(n){window.runtime.LogInfo(n)}function f(n){window.runtime.LogWarning(n)}function x(n){window.runtime.LogError(n)}function s(n){window.runtime.LogFatal(n)}var w={};t(w,{EventsEmit:()=>g,EventsOn:()=>a,EventsOnMultiple:()=>l,EventsOnce:()=>S});function l(n,i,o){window.runtime.EventsOnMultiple(n,i,o)}function a(n,i){OnMultiple(n,i,-1)}function S(n,i){OnMultiple(n,i,1)}function g(n){let i=[n].slice.call(arguments);return window.runtime.EventsEmit.apply(null,i)}var r={};t(r,{WindowCenter:()=>M,WindowFullscreen:()=>v,WindowGetPosition:()=>B,WindowGetSize:()=>O,WindowHide:()=>P,WindowMaximise:()=>b,WindowMinimise:()=>A,WindowReload:()=>L,WindowSetMaxSize:()=>F,WindowSetMinSize:()=>G,WindowSetPosition:()=>R,WindowSetRGBA:()=>D,WindowSetSize:()=>U,WindowSetTitle:()=>E,WindowShow:()=>T,WindowUnFullscreen:()=>z,WindowUnmaximise:()=>h,WindowUnminimise:()=>C});function L(){window.runtime.WindowReload()}function M(){window.runtime.WindowCenter()}function E(n){window.runtime.WindowSetTitle(n)}function v(){window.runtime.WindowFullscreen()}function z(){window.runtime.WindowUnFullscreen()}function O(){window.runtime.WindowGetSize()}function U(n,i){window.runtime.WindowSetSize(n,i)}function F(n,i){window.runtime.WindowSetMaxSize(n,i)}function G(n,i){window.runtime.WindowSetMinSize(n,i)}function R(n,i){window.runtime.WindowSetPosition(n,i)}function B(){window.runtime.WindowGetPosition()}function P(){window.runtime.WindowHide()}function T(){window.runtime.WindowShow()}function b(){window.runtime.WindowMaximise()}function h(){window.runtime.WindowUnmaximise()}function A(){window.runtime.WindowMinimise()}function C(){window.runtime.WindowUnminimise()}function D(n){window.runtime.WindowSetRGBA(n)}var u={};t(u,{BrowserOpenURL:()=>H});function H(n){window.runtime.BrowserOpenURL(n)}function I(){window.runtime.Quit()}var y={...e,...w,...r,...u,Quit:I};})(); diff --git a/v2/internal/frontend/runtime/wrapper/window.js b/v2/internal/frontend/runtime/wrapper/window.js new file mode 100644 index 000000000..2ef54e3ac --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/window.js @@ -0,0 +1,187 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 9 */ + +/** + * Reloads the Window + * + * @export + */ +export function WindowReload() { + window.runtime.WindowReload(); +} + +/** + * Place the window in the center of the screen + * + * @export + */ +export function WindowCenter() { + window.runtime.WindowCenter(); +} + +/** + * Sets the window title + * + * @param {string} title + * @export + */ +export function WindowSetTitle(title) { + window.runtime.WindowSetTitle(title); +} + +/** + * Makes the window go fullscreen + * + * @export + */ +export function WindowFullscreen() { + window.runtime.WindowFullscreen(); +} + +/** + * Reverts the window from fullscreen + * + * @export + */ +export function WindowUnFullscreen() { + window.runtime.WindowUnFullscreen(); +} + +/** + * Get the Size of the window + * + * @export + * @return {Promise<{w: number, h: number}>} The size of the window + + */ +export function WindowGetSize() { + window.runtime.WindowGetSize(); +} + + +/** + * Set the Size of the window + * + * @export + * @param {number} width + * @param {number} height + */ +export function WindowSetSize(width, height) { + window.runtime.WindowSetSize(width, height); +} + +/** + * Set the maximum size of the window + * + * @export + * @param {number} width + * @param {number} height + */ +export function WindowSetMaxSize(width, height) { + window.runtime.WindowSetMaxSize(width, height); +} + +/** + * Set the minimum size of the window + * + * @export + * @param {number} width + * @param {number} height + */ +export function WindowSetMinSize(width, height) { + window.runtime.WindowSetMinSize(width, height); +} + +/** + * Set the Position of the window + * + * @export + * @param {number} x + * @param {number} y + */ +export function WindowSetPosition(x, y) { + window.runtime.WindowSetPosition(x, y); +} + +/** + * Get the Position of the window + * + * @export + * @return {Promise<{x: number, y: number}>} The position of the window + */ +export function WindowGetPosition() { + window.runtime.WindowGetPosition(); +} + +/** + * Hide the Window + * + * @export + */ +export function WindowHide() { + window.runtime.WindowHide(); +} + +/** + * Show the Window + * + * @export + */ +export function WindowShow() { + window.runtime.WindowShow(); +} + +/** + * Maximise the Window + * + * @export + */ +export function WindowMaximise() { + window.runtime.WindowMaximise(); +} + +/** + * Unmaximise the Window + * + * @export + */ +export function WindowUnmaximise() { + window.runtime.WindowUnmaximise(); +} + +/** + * Minimise the Window + * + * @export + */ +export function WindowMinimise() { + window.runtime.WindowMinimise(); +} + +/** + * Unminimise the Window + * + * @export + */ +export function WindowUnminimise() { + window.runtime.WindowUnminimise(); +} + +/** + * Sets the background colour of the window + * + * @export + * @param {RGBA} RGBA background colour + */ +export function WindowSetRGBA(RGBA) { + window.runtime.WindowSetRGBA(RGBA); +} diff --git a/v2/internal/frontend/runtime/wrapper/wrapper.go b/v2/internal/frontend/runtime/wrapper/wrapper.go new file mode 100644 index 000000000..94853bc7c --- /dev/null +++ b/v2/internal/frontend/runtime/wrapper/wrapper.go @@ -0,0 +1,6 @@ +package wrapper + +import "embed" + +//go:embed runtime.js runtime.d.ts package.json +var RuntimeWrapper embed.FS diff --git a/v2/internal/fs/fs.go b/v2/internal/fs/fs.go new file mode 100644 index 000000000..9bb192209 --- /dev/null +++ b/v2/internal/fs/fs.go @@ -0,0 +1,397 @@ +package fs + +import ( + "crypto/md5" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "unsafe" + + "github.com/leaanthony/slicer" +) + +// LocalDirectory gets the caller's file directory +// Equivalent to node's __DIRNAME +func LocalDirectory() string { + _, thisFile, _, _ := runtime.Caller(1) + return filepath.Dir(thisFile) +} + +// RelativeToCwd returns an absolute path based on the cwd +// and the given relative path +func RelativeToCwd(relativePath string) (string, error) { + cwd, err := os.Getwd() + if err != nil { + return "", err + } + + return filepath.Join(cwd, relativePath), nil +} + +// Mkdir will create the given directory +func Mkdir(dirname string) error { + return os.Mkdir(dirname, 0755) +} + +// MkDirs creates the given nested directories. +// Returns error on failure +func MkDirs(fullPath string, mode ...os.FileMode) error { + var perms os.FileMode + perms = 0755 + if len(mode) == 1 { + perms = mode[0] + } + return os.MkdirAll(fullPath, perms) +} + +// MoveFile attempts to move the source file to the target +// Target is a fully qualified path to a file *name*, not a +// directory +func MoveFile(source string, target string) error { + return os.Rename(source, target) +} + +// DeleteFile will delete the given file +func DeleteFile(filename string) error { + return os.Remove(filename) +} + +// CopyFile from source to target +func CopyFile(source string, target string) error { + s, err := os.Open(source) + if err != nil { + return err + } + defer s.Close() + d, err := os.Create(target) + if err != nil { + return err + } + if _, err := io.Copy(d, s); err != nil { + d.Close() + return err + } + return d.Close() +} + +// DirExists - Returns true if the given path resolves to a directory on the filesystem +func DirExists(path string) bool { + fi, err := os.Lstat(path) + if err != nil { + return false + } + + return fi.Mode().IsDir() +} + +// FileExists returns a boolean value indicating whether +// the given file exists +func FileExists(path string) bool { + fi, err := os.Lstat(path) + if err != nil { + return false + } + + return fi.Mode().IsRegular() +} + +// RelativePath returns a qualified path created by joining the +// directory of the calling file and the given relative path. +// +// Example: RelativePath("..") in *this* file would give you '/path/to/wails2/v2/internal` +func RelativePath(relativepath string, optionalpaths ...string) string { + _, thisFile, _, _ := runtime.Caller(1) + localDir := filepath.Dir(thisFile) + + // If we have optional paths, join them to the relativepath + if len(optionalpaths) > 0 { + paths := []string{relativepath} + paths = append(paths, optionalpaths...) + relativepath = filepath.Join(paths...) + } + result, err := filepath.Abs(filepath.Join(localDir, relativepath)) + if err != nil { + // I'm allowing this for 1 reason only: It's fatal if the path + // supplied is wrong as it's only used internally in Wails. If we get + // that path wrong, we should know about it immediately. The other reason is + // that it cuts down a ton of unnecassary error handling. + panic(err) + } + return result +} + +// MustLoadString attempts to load a string and will abort with a fatal message if +// something goes wrong +func MustLoadString(filename string) string { + data, err := ioutil.ReadFile(filename) + if err != nil { + fmt.Printf("FATAL: Unable to load file '%s': %s\n", filename, err.Error()) + os.Exit(1) + } + return *(*string)(unsafe.Pointer(&data)) +} + +// MD5File returns the md5sum of the given file +func MD5File(filename string) (string, error) { + f, err := os.Open(filename) + if err != nil { + return "", err + } + defer f.Close() + + h := md5.New() + if _, err := io.Copy(h, f); err != nil { + return "", err + } + + return fmt.Sprintf("%x", h.Sum(nil)), nil +} + +// MustMD5File will call MD5File and abort the program on error +func MustMD5File(filename string) string { + result, err := MD5File(filename) + if err != nil { + println("FATAL: Unable to MD5Sum file:", err.Error()) + os.Exit(1) + } + return result +} + +// MustWriteString will attempt to write the given data to the given filename +// It will abort the program in the event of a failure +func MustWriteString(filename string, data string) { + err := ioutil.WriteFile(filename, []byte(data), 0755) + if err != nil { + fatal("Unable to write file", filename, ":", err.Error()) + os.Exit(1) + } +} + +// fatal will print the optional messages and die +func fatal(message ...string) { + if len(message) > 0 { + print("FATAL:") + for text := range message { + print(text) + } + } + os.Exit(1) +} + +// GetSubdirectories returns a list of subdirectories for the given root directory +func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) { + var result slicer.StringSlicer + + // Iterate root dir + err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + // If we have a directory, save it + if info.IsDir() { + result.Add(path) + } + return nil + }) + return &result, err +} + +func DirIsEmpty(dir string) (bool, error) { + + // CREDIT: https://stackoverflow.com/a/30708914/8325411 + f, err := os.Open(dir) + if err != nil { + return false, err + } + defer f.Close() + + _, err = f.Readdirnames(1) // Or f.Readdir(1) + if err == io.EOF { + return true, nil + } + return false, err // Either not empty or error, suits both cases +} + +// CopyDir recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. +// Symlinks are ignored and skipped. +// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 +func CopyDir(src string, dst string) (err error) { + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + if err != nil { + return err + } + if !si.IsDir() { + return fmt.Errorf("source is not a directory") + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return + } + if err == nil { + return fmt.Errorf("destination already exists") + } + + err = MkDirs(dst) + if err != nil { + return + } + + entries, err := ioutil.ReadDir(src) + if err != nil { + return + } + + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + err = CopyDir(srcPath, dstPath) + if err != nil { + return + } + } else { + // Skip symlinks. + if entry.Mode()&os.ModeSymlink != 0 { + continue + } + + err = CopyFile(srcPath, dstPath) + if err != nil { + return + } + } + } + + return +} + +// CopyDirExtended recursively copies a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. It ignores any files or +// directories that are given through the ignore parameter. +// Symlinks are ignored and skipped. +// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04 +func CopyDirExtended(src string, dst string, ignore []string) (err error) { + + ignoreList := slicer.String(ignore) + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + if err != nil { + return err + } + if !si.IsDir() { + return fmt.Errorf("source is not a directory") + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return + } + if err == nil { + return fmt.Errorf("destination already exists") + } + + err = MkDirs(dst) + if err != nil { + return + } + + entries, err := ioutil.ReadDir(src) + if err != nil { + return + } + + for _, entry := range entries { + if ignoreList.Contains(entry.Name()) { + continue + } + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + if entry.IsDir() { + err = CopyDir(srcPath, dstPath) + if err != nil { + return + } + } else { + // Skip symlinks. + if entry.Mode()&os.ModeSymlink != 0 { + continue + } + + err = CopyFile(srcPath, dstPath) + if err != nil { + return + } + } + } + + return +} + +// MoveDirExtended recursively moves a directory tree, attempting to preserve permissions. +// Source directory must exist, destination directory must *not* exist. It ignores any files or +// directories that are given through the ignore parameter. +// Symlinks are ignored and skipped. +func MoveDirExtended(src string, dst string, ignore []string) (err error) { + + ignoreList := slicer.String(ignore) + src = filepath.Clean(src) + dst = filepath.Clean(dst) + + si, err := os.Stat(src) + if err != nil { + return err + } + if !si.IsDir() { + return fmt.Errorf("source is not a directory") + } + + _, err = os.Stat(dst) + if err != nil && !os.IsNotExist(err) { + return + } + if err == nil { + return fmt.Errorf("destination already exists") + } + + err = MkDirs(dst) + if err != nil { + return + } + + entries, err := ioutil.ReadDir(src) + if err != nil { + return + } + + for _, entry := range entries { + if ignoreList.Contains(entry.Name()) { + continue + } + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + + // Skip symlinks. + if entry.Mode()&os.ModeSymlink != 0 { + continue + } + + err := os.Rename(srcPath, dstPath) + if err != nil { + return err + } + } + + return +} diff --git a/v2/internal/fs/fs_test.go b/v2/internal/fs/fs_test.go new file mode 100644 index 000000000..2cc524123 --- /dev/null +++ b/v2/internal/fs/fs_test.go @@ -0,0 +1,31 @@ +package fs + +import ( + "os" + "path/filepath" + "testing" + + "github.com/matryer/is" +) + +func TestRelativePath(t *testing.T) { + + is := is.New(t) + + cwd, err := os.Getwd() + is.Equal(err, nil) + + // Check current directory + actual := RelativePath(".") + is.Equal(actual, cwd) + + // Check 2 parameters + actual = RelativePath("..", "fs") + is.Equal(actual, cwd) + + // Check 3 parameters including filename + actual = RelativePath("..", "fs", "fs.go") + expected := filepath.Join(cwd, "fs.go") + is.Equal(actual, expected) + +} diff --git a/v2/internal/github/github.go b/v2/internal/github/github.go new file mode 100644 index 000000000..e91b296e1 --- /dev/null +++ b/v2/internal/github/github.go @@ -0,0 +1,103 @@ +package github + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "sort" + "strings" +) + +// GetVersionTags gets the list of tags on the Wails repo +// It returns a list of sorted tags in descending order +func GetVersionTags() ([]*SemanticVersion, error) { + + result := []*SemanticVersion{} + var err error + + resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags") + if err != nil { + return result, err + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return result, err + } + + data := []map[string]interface{}{} + err = json.Unmarshal(body, &data) + if err != nil { + return result, err + } + + // Convert tag data to Version structs + for _, tag := range data { + version := tag["name"].(string) + if !strings.HasPrefix(version, "v2") { + continue + } + semver, err := NewSemanticVersion(version) + if err != nil { + return result, err + } + result = append(result, semver) + } + + // Reverse Sort + sort.Sort(sort.Reverse(SemverCollection(result))) + + return result, err +} + +// GetLatestStableRelease gets the latest stable release on GitHub +func GetLatestStableRelease() (result *SemanticVersion, err error) { + + tags, err := GetVersionTags() + if err != nil { + return nil, err + } + + for _, tag := range tags { + if tag.IsRelease() { + return tag, nil + } + } + + return nil, fmt.Errorf("no release tag found") +} + +// GetLatestPreRelease gets the latest prerelease on GitHub +func GetLatestPreRelease() (result *SemanticVersion, err error) { + + tags, err := GetVersionTags() + if err != nil { + return nil, err + } + + for _, tag := range tags { + if tag.IsPreRelease() { + return tag, nil + } + } + + return nil, fmt.Errorf("no prerelease tag found") +} + +// IsValidTag returns true if the given string is a valid tag +func IsValidTag(tagVersion string) (bool, error) { + if tagVersion[0] == 'v' { + tagVersion = tagVersion[1:] + } + tags, err := GetVersionTags() + if err != nil { + return false, err + } + + for _, tag := range tags { + if tag.String() == tagVersion { + return true, nil + } + } + return false, nil +} diff --git a/v2/internal/github/semver.go b/v2/internal/github/semver.go new file mode 100644 index 000000000..1cf5907fa --- /dev/null +++ b/v2/internal/github/semver.go @@ -0,0 +1,106 @@ +package github + +import ( + "fmt" + + "github.com/Masterminds/semver" +) + +// SemanticVersion is a struct containing a semantic version +type SemanticVersion struct { + Version *semver.Version +} + +// NewSemanticVersion creates a new SemanticVersion object with the given version string +func NewSemanticVersion(version string) (*SemanticVersion, error) { + semverVersion, err := semver.NewVersion(version) + if err != nil { + return nil, err + } + return &SemanticVersion{ + Version: semverVersion, + }, nil +} + +// IsRelease returns true if it's a release version +func (s *SemanticVersion) IsRelease() bool { + // Limit to v2 + if s.Version.Major() != 2 { + return false + } + return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0 +} + +// IsPreRelease returns true if it's a prerelease version +func (s *SemanticVersion) IsPreRelease() bool { + // Limit to v1 + if s.Version.Major() != 2 { + return false + } + return len(s.Version.Prerelease()) > 0 +} + +func (s *SemanticVersion) String() string { + return s.Version.String() +} + +// IsGreaterThan returns true if this version is greater than the given version +func (s *SemanticVersion) IsGreaterThan(version *SemanticVersion) (bool, error) { + // Set up new constraint + constraint, err := semver.NewConstraint("> " + version.Version.String()) + if err != nil { + return false, err + } + + // Check if the desired one is greater than the requested on + success, msgs := constraint.Validate(s.Version) + if !success { + return false, msgs[0] + } + return true, nil +} + +// IsGreaterThanOrEqual returns true if this version is greater than or equal the given version +func (s *SemanticVersion) IsGreaterThanOrEqual(version *SemanticVersion) (bool, error) { + // Set up new constraint + constraint, err := semver.NewConstraint(">= " + version.Version.String()) + if err != nil { + return false, err + } + + // Check if the desired one is greater than the requested on + success, msgs := constraint.Validate(s.Version) + if !success { + return false, msgs[0] + } + return true, nil +} + +// MainVersion returns the main version of any version+prerelease+metadata +// EG: MainVersion("1.2.3-pre") => "1.2.3" +func (s *SemanticVersion) MainVersion() *SemanticVersion { + mainVersion := fmt.Sprintf("%d.%d.%d", s.Version.Major(), s.Version.Minor(), s.Version.Patch()) + result, _ := NewSemanticVersion(mainVersion) + return result +} + +// SemverCollection is a collection of SemanticVersion objects +type SemverCollection []*SemanticVersion + +// Len returns the length of a collection. The number of Version instances +// on the slice. +func (c SemverCollection) Len() int { + return len(c) +} + +// Less is needed for the sort interface to compare two Version objects on the +// slice. If checks if one is less than the other. +func (c SemverCollection) Less(i, j int) bool { + return c[i].Version.LessThan(c[j].Version) +} + +// Swap is needed for the sort interface to replace the Version objects +// at two different positions in the slice. +func (c SemverCollection) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} diff --git a/v2/internal/github/semver_test.go b/v2/internal/github/semver_test.go new file mode 100644 index 000000000..4f8f9a693 --- /dev/null +++ b/v2/internal/github/semver_test.go @@ -0,0 +1,32 @@ +package github + +import ( + "github.com/matryer/is" + "testing" +) + +func TestSemanticVersion_IsGreaterThan(t *testing.T) { + is2 := is.New(t) + + alpha1, err := NewSemanticVersion("v2.0.0-alpha.1") + is2.NoErr(err) + + beta1, err := NewSemanticVersion("v2.0.0-beta.1") + is2.NoErr(err) + + v2, err := NewSemanticVersion("v2.0.0") + is2.NoErr(err) + + is2.True(alpha1.IsPreRelease()) + is2.True(beta1.IsPreRelease()) + is2.True(!v2.IsPreRelease()) + is2.True(v2.IsRelease()) + + result, err := beta1.IsGreaterThan(alpha1) + is2.NoErr(err) + is2.True(result) + + result, err = v2.IsGreaterThan(beta1) + is2.NoErr(err) + is2.True(result) +} diff --git a/v2/internal/gomod/gomod.go b/v2/internal/gomod/gomod.go new file mode 100644 index 000000000..c043d1e8f --- /dev/null +++ b/v2/internal/gomod/gomod.go @@ -0,0 +1,79 @@ +package gomod + +import ( + "fmt" + "github.com/Masterminds/semver" + "golang.org/x/mod/modfile" +) + +func GetWailsVersionFromModFile(goModText []byte) (*semver.Version, error) { + file, err := modfile.Parse("", goModText, nil) + if err != nil { + return nil, err + } + + for _, req := range file.Require { + if req.Syntax == nil { + continue + } + tokenPosition := 0 + if !req.Syntax.InBlock { + tokenPosition = 1 + } + if req.Syntax.Token[tokenPosition] == "github.com/wailsapp/wails/v2" { + version := req.Syntax.Token[tokenPosition+1] + return semver.NewVersion(version) + } + } + + return nil, nil +} + +func GoModOutOfSync(goModData []byte, currentVersion string) (bool, error) { + gomodversion, err := GetWailsVersionFromModFile(goModData) + if err != nil { + return false, err + } + result, err := semver.NewVersion(currentVersion) + if err != nil || result == nil { + return false, fmt.Errorf("Unable to parse Wails version: %s", currentVersion) + } + + return !gomodversion.Equal(result), nil +} + +func UpdateGoModVersion(goModText []byte, currentVersion string) ([]byte, error) { + file, err := modfile.Parse("", goModText, nil) + if err != nil { + return nil, err + } + + err = file.AddRequire("github.com/wailsapp/wails/v2", currentVersion) + if err != nil { + return nil, err + } + + // Replace + if len(file.Replace) == 0 { + return file.Format() + } + + for _, req := range file.Replace { + if req.Syntax == nil { + continue + } + tokenPosition := 0 + if !req.Syntax.InBlock { + tokenPosition = 1 + } + if req.Syntax.Token[tokenPosition] == "github.com/wailsapp/wails/v2" { + version := req.Syntax.Token[tokenPosition+1] + _, err := semver.NewVersion(version) + if err == nil { + req.Syntax.Token[tokenPosition+1] = currentVersion + } + } + } + + return file.Format() +} diff --git a/v2/internal/gomod/gomod_test.go b/v2/internal/gomod/gomod_test.go new file mode 100644 index 000000000..7dc16fb34 --- /dev/null +++ b/v2/internal/gomod/gomod_test.go @@ -0,0 +1,603 @@ +package gomod + +import ( + "github.com/Masterminds/semver" + "github.com/matryer/is" + "reflect" + "testing" +) + +const basic string = `module changeme + +go 1.17 + +require github.com/wailsapp/wails/v2 v2.0.0-beta.7 + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` + +func TestGetWailsVersion(t *testing.T) { + tests := []struct { + name string + goModText []byte + want *semver.Version + wantErr bool + }{ + {"basic", []byte(basic), semver.MustParse("v2.0.0-beta.7"), false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetWailsVersionFromModFile(tt.goModText) + if (err != nil) != tt.wantErr { + t.Errorf("GetWailsVersion() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetWailsVersion() got = %v, want %v", got, tt.want) + } + }) + } +} + +const basicUpdated string = `module changeme + +go 1.17 + +require github.com/wailsapp/wails/v2 v2.0.0-beta.15 + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` + +const multilineRequire = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.7 +) +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` +const multilineReplace = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.7 +) +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` + +const multilineReplaceNoVersion = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.7 +) +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` + +const multilineReplaceNoVersionBlock = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.7 +) +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace ( + github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +) +` + +const multilineReplaceBlock = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.7 +) +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace ( + github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +) +` + +const multilineRequireUpdated = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.15 +) + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +//replace github.com/wailsapp/wails/v2 v2.0.0-beta.7 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` + +func TestUpdateGoModVersion(t *testing.T) { + is2 := is.New(t) + + type args struct { + goModText []byte + currentVersion string + } + tests := []struct { + name string + args args + want []byte + wantErr bool + }{ + {"basic", args{[]byte(basic), "v2.0.0-beta.15"}, []byte(basicUpdated), false}, + {"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.15"}, []byte(multilineRequireUpdated), false}, + {"basicmultilinereplace", args{[]byte(multilineReplace), "v2.0.0-beta.15"}, []byte(multilineReplaceUpdated), false}, + {"basicmultilinereplaceblock", args{[]byte(multilineReplaceBlock), "v2.0.0-beta.15"}, []byte(multilineReplaceBlockUpdated), false}, + {"basicmultilinereplacenoversion", args{[]byte(multilineReplaceNoVersion), "v2.0.0-beta.15"}, []byte(multilineReplaceNoVersionUpdated), false}, + {"basicmultilinereplacenoversionblock", args{[]byte(multilineReplaceNoVersionBlock), "v2.0.0-beta.15"}, []byte(multilineReplaceNoVersionBlockUpdated), false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := UpdateGoModVersion(tt.args.goModText, tt.args.currentVersion) + if (err != nil) != tt.wantErr { + t.Errorf("UpdateGoModVersion() error = %v, wantErr %v", err, tt.wantErr) + return + } + is2.Equal(got, tt.want) + }) + } +} + +func TestGoModOutOfSync(t *testing.T) { + is2 := is.New(t) + + type args struct { + goModData []byte + currentVersion string + } + tests := []struct { + name string + args args + want bool + wantErr bool + }{ + {"basic", args{[]byte(basic), "v2.0.0-beta.15"}, true, false}, + {"basicmultiline", args{[]byte(multilineRequire), "v2.0.0-beta.15"}, true, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GoModOutOfSync(tt.args.goModData, tt.args.currentVersion) + if (err != nil) != tt.wantErr { + t.Errorf("GoModOutOfSync() error = %v, wantErr %v", err, tt.wantErr) + return + } + is2.Equal(got, tt.want) + }) + } +} + +const multilineReplaceUpdated = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.15 +) + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` +const multilineReplaceNoVersionUpdated = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.15 +) + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +` +const multilineReplaceNoVersionBlockUpdated = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.15 +) + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace ( + github.com/wailsapp/wails/v2 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +) +` + +const multilineReplaceBlockUpdated = `module changeme + +go 1.17 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-beta.15 +) + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fasthttp/websocket v0.0.0-20200320073529-1554a54587ab // indirect + github.com/gabriel-vasile/mimetype v1.3.1 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/gofiber/fiber/v2 v2.17.0 // indirect + github.com/gofiber/websocket/v2 v2.0.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/imdario/mergo v0.3.12 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/leaanthony/debme v1.2.1 // indirect + github.com/leaanthony/go-ansi-parser v1.0.1 // indirect + github.com/leaanthony/go-common-file-dialog v1.0.3 // indirect + github.com/leaanthony/go-webview2 v0.0.0-20211007092718-65d2f028ef2d // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.5.0 // indirect + github.com/leaanthony/typescriptify-golang-structs v0.1.7 // indirect + github.com/leaanthony/webview2runtime v1.1.0 // indirect + github.com/leaanthony/winc v0.0.0-20210921073452-54963136bf18 // indirect + github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f // indirect + github.com/tkrajina/go-reflector v0.5.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.28.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect +) + +replace ( + github.com/wailsapp/wails/v2 v2.0.0-beta.15 => C:\Users\leaan\Documents\wails-v2-beta\wails\v2 +) +` diff --git a/v2/internal/html/asset.go b/v2/internal/html/asset.go new file mode 100644 index 000000000..4c41358f4 --- /dev/null +++ b/v2/internal/html/asset.go @@ -0,0 +1,128 @@ +package html + +import ( + "fmt" + "io/ioutil" + "log" + "net/url" + "path/filepath" + "regexp" + "strings" + "unsafe" + + "github.com/tdewolff/minify" + "github.com/tdewolff/minify/js" +) + +type assetTypes struct { + JS string + CSS string + FAVICON string + HTML string +} + +// AssetTypes is an enum for the asset type keys +var AssetTypes *assetTypes = &assetTypes{ + JS: "javascript", + CSS: "css", + FAVICON: "favicon", + HTML: "html", +} + +// Asset describes an asset type and its path +type Asset struct { + Type string + Path string + Data string +} + +// Load the asset from disk +func (a *Asset) Load(basedirectory string) error { + assetpath := filepath.Join(basedirectory, a.Path) + data, err := ioutil.ReadFile(assetpath) + if err != nil { + return err + } + a.Data = string(data) + return nil +} + +// AsString returns the data as a READ ONLY string +func (a *Asset) AsString() string { + return a.Data +} + +func (a *Asset) minifiedData() (string, error) { + switch a.Type { + case AssetTypes.HTML: + + // Escape HTML + var re = regexp.MustCompile(`[\s]+`) + result := re.ReplaceAllString(a.Data, ` `) + + // Inject wailsloader code + result = strings.Replace(result, ``, ``, 1) + + url := url.URL{Path: result} + urlString := strings.ReplaceAll(url.String(), "/", "%2f") + + // Save Data uRI string + return "data:text/html;charset=utf-8," + urlString, nil + + case AssetTypes.CSS: + + // Escape CSS data + var re = regexp.MustCompile(`\s{2,}`) + result := re.ReplaceAllString(a.Data, ``) + result = strings.ReplaceAll(result, "\n", "") + result = strings.ReplaceAll(result, "\r\n", "") + result = strings.ReplaceAll(result, "\n", "") + result = strings.ReplaceAll(result, "\t", "") + result = strings.ReplaceAll(result, `\`, `\\`) + result = strings.ReplaceAll(result, `"`, `\"`) + result = strings.ReplaceAll(result, `'`, `\'`) + result = strings.ReplaceAll(result, ` {`, `{`) + result = strings.ReplaceAll(result, `: `, `:`) + return fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result), nil + + case AssetTypes.JS: + m := minify.New() + m.AddFunc("application/javascript", js.Minify) + var err error + result, err := m.String("application/javascript", a.Data+";") + if err != nil { + return "", err + } + return result, nil + default: + return "", fmt.Errorf("minification for asset type %s not implemented", a.Type) + } +} + +// AsCHexData processes the asset data so it may be used by C +func (a *Asset) AsCHexData() string { + dataString, err := a.minifiedData() + if err != nil { + log.Fatal(err) + } + // Get byte data of the string + bytes := *(*[]byte)(unsafe.Pointer(&dataString)) + + // Create a strings builder + var cdata strings.Builder + + // Set buffer size to 4k + cdata.Grow(4096) + + // Convert each byte to hex + for _, b := range bytes { + cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + } + + return cdata.String() +} + +// Dump will output the asset to the terminal +func (a *Asset) Dump() { + fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10]) +} diff --git a/v2/internal/html/asset_test.go b/v2/internal/html/asset_test.go new file mode 100644 index 000000000..f743b4ba9 --- /dev/null +++ b/v2/internal/html/asset_test.go @@ -0,0 +1,53 @@ +package html + +import "testing" + +func TestAsset_minifiedData(t *testing.T) { + type fields struct { + Type string + Path string + Data string + } + tests := []struct { + name string + fields fields + want string + wantErr bool + }{ + { + name: "multi-line tag", + fields: fields{ + Type: AssetTypes.HTML, + Path: "foo.html", + Data: "\n", + }, + want: "data:text/html;charset=utf-8,%3Clink%20rel=%22stylesheet%22%20href=%22src%2ffoo.css%22%20%3E%20", + }, + { + name: "multi-line tag no spaces", + fields: fields{ + Type: AssetTypes.HTML, + Path: "foo.html", + Data: "\n", + }, + want: "data:text/html;charset=utf-8,%3Clink%20rel=%22stylesheet%22%20href=%22src%2ffoo.css%22%20%3E%20", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &Asset{ + Type: tt.fields.Type, + Path: tt.fields.Path, + Data: tt.fields.Data, + } + got, err := a.minifiedData() + if (err != nil) != tt.wantErr { + t.Errorf("Asset.minifiedData() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("Asset.minifiedData() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/v2/internal/html/assetbundle.go b/v2/internal/html/assetbundle.go new file mode 100644 index 000000000..6ec098c7e --- /dev/null +++ b/v2/internal/html/assetbundle.go @@ -0,0 +1,218 @@ +package html + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "path/filepath" + "strings" + + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/assetdb" + "golang.org/x/net/html" +) + +// AssetBundle is a collection of Assets +type AssetBundle struct { + assets []*Asset + basedirectory string +} + +// NewAssetBundle creates a new AssetBundle struct containing +// the given html and all the assets referenced by it +func NewAssetBundle(pathToHTML string) (*AssetBundle, error) { + + // Create result + result := &AssetBundle{ + basedirectory: filepath.Dir(pathToHTML), + } + + err := result.loadAssets(pathToHTML) + if err != nil { + return nil, err + } + + return result, nil +} + +// loadAssets processes the given html file and loads in +// all referenced assets +func (a *AssetBundle) loadAssets(pathToHTML string) error { + + // Save HTML + htmlAsset := &Asset{ + Type: AssetTypes.HTML, + Path: filepath.Base(pathToHTML), + } + err := htmlAsset.Load(a.basedirectory) + if err != nil { + return err + } + a.assets = append(a.assets, htmlAsset) + + return a.processHTML(htmlAsset.AsString()) +} + +// Credit to: https://drstearns.github.io/tutorials/tokenizing/ +func (a *AssetBundle) processHTML(htmldata string) error { + + // Tokenize the html + buf := bytes.NewBufferString(htmldata) + tokenizer := html.NewTokenizer(buf) + + paths := slicer.String() + + for { + //get the next token type + tokenType := tokenizer.Next() + + //if it's an error token, we either reached + //the end of the file, or the HTML was malformed + if tokenType == html.ErrorToken { + err := tokenizer.Err() + if err == io.EOF { + //end of the file, break out of the loop + break + } + //otherwise, there was an error tokenizing, + //which likely means the HTML was malformed. + //since this is a simple command-line utility, + //we can just use log.Fatalf() to report the error + //and exit the process with a non-zero status code + return tokenizer.Err() + } + + //process the token according to the token type... + if tokenType == html.StartTagToken || tokenType == html.SelfClosingTagToken { + //get the token + token := tokenizer.Token() + + //if the name of the element is "title" + if "link" == token.Data { + //the next token should be the page title + tokenType = tokenizer.Next() + //just make sure it's actually a text token + asset := &Asset{} + for _, attr := range token.Attr { + // Favicon + if attr.Key == "rel" && attr.Val == "icon" { + asset.Type = AssetTypes.FAVICON + } + if attr.Key == "href" { + asset.Path = attr.Val + } + // standard stylesheet + if attr.Key == "rel" && attr.Val == "stylesheet" { + asset.Type = AssetTypes.CSS + } + if attr.Key == "as" && attr.Val == "style" { + asset.Type = AssetTypes.CSS + } + if attr.Key == "as" && attr.Val == "script" { + asset.Type = AssetTypes.JS + } + if attr.Key == "rel" && attr.Val == "modulepreload" { + asset.Type = AssetTypes.JS + } + } + + // Ensure we don't include duplicates + if !paths.Contains(asset.Path) { + err := asset.Load(a.basedirectory) + if err != nil { + return err + } + a.assets = append(a.assets, asset) + paths.Add(asset.Path) + } + } + if "script" == token.Data { + tokenType = tokenizer.Next() + //just make sure it's actually a text token + asset := &Asset{Type: AssetTypes.JS} + for _, attr := range token.Attr { + if attr.Key == "src" { + asset.Path = attr.Val + break + } + } + if !paths.Contains(asset.Path) && asset.Path != "" { + err := asset.Load(a.basedirectory) + if err != nil { + return err + } + a.assets = append(a.assets, asset) + paths.Add(asset.Path) + } + } + } + } + + return nil +} + +// WriteToCFile dumps all the assets to C files in the given directory +func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) { + + // Write out the assets.c file + var cdata strings.Builder + + // Write header + header := `// assets.h +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. +// This file was auto-generated. DO NOT MODIFY. + +` + cdata.WriteString(header) + + // Loop over the Assets + var err error + assetVariables := slicer.String() + var variableName string + for index, asset := range a.assets { + // For desktop we ignore the favicon + if asset.Type == AssetTypes.FAVICON { + continue + } + variableName = fmt.Sprintf("%s%d", asset.Type, index) + assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData()) + cdata.WriteString(assetCdata) + assetVariables.Add(variableName) + } + + if assetVariables.Length() > 0 { + cdata.WriteString(fmt.Sprintf("\nconst unsigned char *assets[] = { %s, 0x00 };", assetVariables.Join(", "))) + } else { + cdata.WriteString("\nconst unsigned char *assets[] = { 0x00 };") + } + + // Save file + assetsFile := filepath.Join(targetDir, "assets.h") + err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600) + if err != nil { + return "", err + } + return assetsFile, nil +} + +// ConvertToAssetDB returns an assetdb.AssetDB initialized with +// the items in the AssetBundle +func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) { + theassetdb := assetdb.NewAssetDB() + + // Loop over the Assets + for _, asset := range a.assets { + theassetdb.AddAsset(asset.Path, []byte(asset.Data)) + } + + return theassetdb, nil +} + +// Dump will output the assets to the terminal +func (a *AssetBundle) Dump() { + println("Assets:") + for _, asset := range a.assets { + asset.Dump() + } +} diff --git a/v2/internal/html/assetbundle_test.go b/v2/internal/html/assetbundle_test.go new file mode 100644 index 000000000..d1009d7ac --- /dev/null +++ b/v2/internal/html/assetbundle_test.go @@ -0,0 +1,84 @@ +package html + +import ( + "testing" +) + +func TestNewAssetBundle(t *testing.T) { + tests := []struct { + name string + pathToHTML string + wantAssets []string + wantErr bool + }{ + { + name: "basic html", + pathToHTML: "testdata/basic.html", + wantAssets: []string{ + AssetTypes.HTML, + AssetTypes.FAVICON, + AssetTypes.JS, + AssetTypes.CSS, + }, + wantErr: false, + }, + { + name: "self closing tags", + pathToHTML: "testdata/self_closing.html", + wantAssets: []string{ + AssetTypes.HTML, + AssetTypes.FAVICON, + AssetTypes.JS, + AssetTypes.CSS, + }, + wantErr: false, + }, + { + name: "multi-line tags", + pathToHTML: "testdata/self_closing.html", + wantAssets: []string{ + AssetTypes.HTML, + AssetTypes.FAVICON, + AssetTypes.JS, + AssetTypes.CSS, + }, + wantErr: false, + }, + { + name: "inline javascript", + pathToHTML: "testdata/inline_javascript.html", + wantAssets: []string{ + AssetTypes.HTML, + AssetTypes.FAVICON, + AssetTypes.JS, + AssetTypes.CSS, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewAssetBundle(tt.pathToHTML) + if (err != nil) != tt.wantErr { + t.Errorf("NewAssetBundle() error = %v, wantErr %v", err, tt.wantErr) + return + } + if len(got.assets) != len(tt.wantAssets) { + t.Errorf("NewAssetBundle() len(assets) = %d, want %d", + len(got.assets), len(tt.wantAssets)) + } + + for i := range tt.wantAssets { + if i >= len(got.assets) { + t.Errorf("NewAssetBundle() missing assets[%d].Type = %s", + i, tt.wantAssets[i]) + } else { + if got.assets[i].Type != tt.wantAssets[i] { + t.Errorf("NewAssetBundle() assets[%d].Type = %s, want %s", + i, got.assets[i].Type, tt.wantAssets[i]) + } + } + } + }) + } +} diff --git a/v2/internal/html/testdata/basic.html b/v2/internal/html/testdata/basic.html new file mode 100644 index 000000000..cdc3770b8 --- /dev/null +++ b/v2/internal/html/testdata/basic.html @@ -0,0 +1,14 @@ + + + + + + + + + Vite App + + +
+ + diff --git a/v2/internal/html/testdata/inline_javascript.html b/v2/internal/html/testdata/inline_javascript.html new file mode 100644 index 000000000..4f97b75a9 --- /dev/null +++ b/v2/internal/html/testdata/inline_javascript.html @@ -0,0 +1,15 @@ + + + + + + + + + Vite App + + + +
+ + diff --git a/v2/internal/html/testdata/multi_line.html b/v2/internal/html/testdata/multi_line.html new file mode 100644 index 000000000..20ac33691 --- /dev/null +++ b/v2/internal/html/testdata/multi_line.html @@ -0,0 +1,17 @@ + + + + + + + + + Vite App + + +
+ + diff --git a/v2/internal/html/testdata/self_closing.html b/v2/internal/html/testdata/self_closing.html new file mode 100644 index 000000000..b5ee9bb78 --- /dev/null +++ b/v2/internal/html/testdata/self_closing.html @@ -0,0 +1,14 @@ + + + + + + + + + Vite App + + +
+ + diff --git a/v2/internal/html/testdata/src/bundle.js b/v2/internal/html/testdata/src/bundle.js new file mode 100644 index 000000000..105067a39 --- /dev/null +++ b/v2/internal/html/testdata/src/bundle.js @@ -0,0 +1 @@ +window.alert("I am JS!"); diff --git a/v2/internal/html/testdata/src/favicon.svg b/v2/internal/html/testdata/src/favicon.svg new file mode 100644 index 000000000..21bc4e85c --- /dev/null +++ b/v2/internal/html/testdata/src/favicon.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/v2/internal/html/testdata/src/style.css b/v2/internal/html/testdata/src/style.css new file mode 100644 index 000000000..f5bd542b8 --- /dev/null +++ b/v2/internal/html/testdata/src/style.css @@ -0,0 +1,3 @@ +.body { + background: blue; +} diff --git a/v2/internal/logger/custom_logger.go b/v2/internal/logger/custom_logger.go new file mode 100644 index 000000000..5e24aa093 --- /dev/null +++ b/v2/internal/logger/custom_logger.go @@ -0,0 +1,96 @@ +package logger + +import ( + "fmt" +) + +// CustomLogger defines what a user can do with a logger +type CustomLogger interface { + // Writeln writes directly to the output with no log level plus line ending + Writeln(message string) + + // Write writes directly to the output with no log level + Write(message string) + + // Trace level logging. Works like Sprintf. + Trace(format string, args ...interface{}) + + // Debug level logging. Works like Sprintf. + Debug(format string, args ...interface{}) + + // Info level logging. Works like Sprintf. + Info(format string, args ...interface{}) + + // Warning level logging. Works like Sprintf. + Warning(format string, args ...interface{}) + + // Error level logging. Works like Sprintf. + Error(format string, args ...interface{}) + + // Fatal level logging. Works like Sprintf. + Fatal(format string, args ...interface{}) +} + +// customLogger is a utlility to log messages to a number of destinations +type customLogger struct { + logger *Logger + name string +} + +// New creates a new customLogger. You may pass in a number of `io.Writer`s that +// are the targets for the logs +func newcustomLogger(logger *Logger, name string) *customLogger { + result := &customLogger{ + name: name, + logger: logger, + } + return result +} + +// Writeln writes directly to the output with no log level +// Appends a carriage return to the message +func (l *customLogger) Writeln(message string) { + l.logger.Writeln(message) +} + +// Write writes directly to the output with no log level +func (l *customLogger) Write(message string) { + l.logger.Write(message) +} + +// Trace level logging. Works like Sprintf. +func (l *customLogger) Trace(format string, args ...interface{}) { + format = fmt.Sprintf("%s | %s", l.name, format) + l.logger.Trace(format, args...) +} + +// Debug level logging. Works like Sprintf. +func (l *customLogger) Debug(format string, args ...interface{}) { + format = fmt.Sprintf("%s | %s", l.name, format) + l.logger.Debug(format, args...) +} + +// Info level logging. Works like Sprintf. +func (l *customLogger) Info(format string, args ...interface{}) { + format = fmt.Sprintf("%s | %s", l.name, format) + l.logger.Info(format, args...) +} + +// Warning level logging. Works like Sprintf. +func (l *customLogger) Warning(format string, args ...interface{}) { + format = fmt.Sprintf("%s | %s", l.name, format) + l.logger.Warning(format, args...) +} + +// Error level logging. Works like Sprintf. +func (l *customLogger) Error(format string, args ...interface{}) { + format = fmt.Sprintf("%s | %s", l.name, format) + l.logger.Error(format, args...) + +} + +// Fatal level logging. Works like Sprintf. +func (l *customLogger) Fatal(format string, args ...interface{}) { + format = fmt.Sprintf("%s | %s", l.name, format) + l.logger.Fatal(format, args...) +} diff --git a/v2/internal/logger/default_logger.go b/v2/internal/logger/default_logger.go new file mode 100644 index 000000000..fe5c05387 --- /dev/null +++ b/v2/internal/logger/default_logger.go @@ -0,0 +1,109 @@ +package logger + +import ( + "fmt" + "os" + + "github.com/wailsapp/wails/v2/pkg/logger" +) + +// LogLevel is an alias for the public LogLevel +type LogLevel = logger.LogLevel + +// Logger is a utlility to log messages to a number of destinations +type Logger struct { + output logger.Logger + logLevel LogLevel + showLevelInLog bool +} + +// New creates a new Logger. You may pass in a number of `io.Writer`s that +// are the targets for the logs +func New(output logger.Logger) *Logger { + if output == nil { + output = logger.NewDefaultLogger() + } + result := &Logger{ + logLevel: logger.INFO, + showLevelInLog: true, + output: output, + } + + return result +} + +// CustomLogger creates a new custom logger that prints out a name/id +// before the messages +func (l *Logger) CustomLogger(name string) CustomLogger { + return newcustomLogger(l, name) +} + +// HideLogLevel removes the loglevel text from the start of each logged line +func (l *Logger) HideLogLevel() { + l.showLevelInLog = true +} + +// SetLogLevel sets the minimum level of logs that will be output +func (l *Logger) SetLogLevel(level LogLevel) { + l.logLevel = level +} + +// Writeln writes directly to the output with no log level +// Appends a carriage return to the message +func (l *Logger) Writeln(message string) { + l.output.Print(message) +} + +// Write writes directly to the output with no log level +func (l *Logger) Write(message string) { + l.output.Print(message) +} + +// Print writes directly to the output with no log level +// Appends a carriage return to the message +func (l *Logger) Print(message string) { + l.Write(message) +} + +// Trace level logging. Works like Sprintf. +func (l *Logger) Trace(format string, args ...interface{}) { + if l.logLevel <= logger.TRACE { + l.output.Trace(fmt.Sprintf(format, args...)) + } +} + +// Debug level logging. Works like Sprintf. +func (l *Logger) Debug(format string, args ...interface{}) { + if l.logLevel <= logger.DEBUG { + l.output.Debug(fmt.Sprintf(format, args...)) + } +} + +// Info level logging. Works like Sprintf. +func (l *Logger) Info(format string, args ...interface{}) { + if l.logLevel <= logger.INFO { + l.output.Info(fmt.Sprintf(format, args...)) + } + +} + +// Warning level logging. Works like Sprintf. +func (l *Logger) Warning(format string, args ...interface{}) { + if l.logLevel <= logger.WARNING { + l.output.Warning(fmt.Sprintf(format, args...)) + } +} + +// Error level logging. Works like Sprintf. +func (l *Logger) Error(format string, args ...interface{}) { + if l.logLevel <= logger.ERROR { + l.output.Error(fmt.Sprintf(format, args...)) + } + +} + +// Fatal level logging. Works like Sprintf. +func (l *Logger) Fatal(format string, args ...interface{}) { + l.output.Fatal(fmt.Sprintf(format, args...)) + os.Exit(1) +} diff --git a/v2/internal/menumanager/applicationmenu.go b/v2/internal/menumanager/applicationmenu.go new file mode 100644 index 000000000..424834e53 --- /dev/null +++ b/v2/internal/menumanager/applicationmenu.go @@ -0,0 +1,51 @@ +package menumanager + +import "github.com/wailsapp/wails/v2/pkg/menu" + +func (m *Manager) SetApplicationMenu(applicationMenu *menu.Menu) error { + + if applicationMenu == nil { + return nil + } + + m.applicationMenu = applicationMenu + + // Reset the menu map + m.applicationMenuItemMap = NewMenuItemMap() + + // Add the menu to the menu map + m.applicationMenuItemMap.AddMenu(applicationMenu) + + return m.processApplicationMenu() +} + +func (m *Manager) GetApplicationMenuJSON() string { + return m.applicationMenuJSON +} + +func (m *Manager) GetProcessedApplicationMenu() *WailsMenu { + return m.processedApplicationMenu +} + +// UpdateApplicationMenu reprocesses the application menu to pick up structure +// changes etc +// Returns the JSON representation of the updated menu +func (m *Manager) UpdateApplicationMenu() (string, error) { + m.applicationMenuItemMap = NewMenuItemMap() + m.applicationMenuItemMap.AddMenu(m.applicationMenu) + err := m.processApplicationMenu() + return m.applicationMenuJSON, err +} + +func (m *Manager) processApplicationMenu() error { + + // Process the menu + m.processedApplicationMenu = NewWailsMenu(m.applicationMenuItemMap, m.applicationMenu) + m.processRadioGroups(m.processedApplicationMenu, m.applicationMenuItemMap) + applicationMenuJSON, err := m.processedApplicationMenu.AsJSON() + if err != nil { + return err + } + m.applicationMenuJSON = applicationMenuJSON + return nil +} diff --git a/v2/internal/menumanager/contextmenu.go b/v2/internal/menumanager/contextmenu.go new file mode 100644 index 000000000..e07cc2222 --- /dev/null +++ b/v2/internal/menumanager/contextmenu.go @@ -0,0 +1,60 @@ +package menumanager + +import ( + "encoding/json" + "fmt" + "github.com/wailsapp/wails/v2/pkg/menu" +) + +type ContextMenu struct { + ID string + ProcessedMenu *WailsMenu + menuItemMap *MenuItemMap + menu *menu.Menu +} + +func (t *ContextMenu) AsJSON() (string, error) { + data, err := json.Marshal(t) + if err != nil { + return "", err + } + return string(data), nil +} + +func NewContextMenu(contextMenu *menu.ContextMenu) *ContextMenu { + + result := &ContextMenu{ + ID: contextMenu.ID, + menu: contextMenu.Menu, + menuItemMap: NewMenuItemMap(), + } + + result.menuItemMap.AddMenu(contextMenu.Menu) + result.ProcessedMenu = NewWailsMenu(result.menuItemMap, result.menu) + + return result +} + +func (m *Manager) AddContextMenu(contextMenu *menu.ContextMenu) { + + newContextMenu := NewContextMenu(contextMenu) + + // Save the references + m.contextMenus[contextMenu.ID] = newContextMenu + m.contextMenuPointers[contextMenu] = contextMenu.ID +} + +func (m *Manager) UpdateContextMenu(contextMenu *menu.ContextMenu) (string, error) { + contextMenuID, contextMenuKnown := m.contextMenuPointers[contextMenu] + if !contextMenuKnown { + return "", fmt.Errorf("unknown Context Menu '%s'. Please add the context menu using AddContextMenu()", contextMenu.ID) + } + + // Create the updated context menu + updatedContextMenu := NewContextMenu(contextMenu) + + // Save the reference + m.contextMenus[contextMenuID] = updatedContextMenu + + return updatedContextMenu.AsJSON() +} diff --git a/v2/internal/menumanager/menuitemmap.go b/v2/internal/menumanager/menuitemmap.go new file mode 100644 index 000000000..790d5d06d --- /dev/null +++ b/v2/internal/menumanager/menuitemmap.go @@ -0,0 +1,75 @@ +package menumanager + +import ( + "fmt" + "github.com/wailsapp/wails/v2/pkg/menu" + "sync" +) + +// MenuItemMap holds a mapping between menuIDs and menu items +type MenuItemMap struct { + idToMenuItemMap map[string]*menu.MenuItem + menuItemToIDMap map[*menu.MenuItem]string + + // We use a simple counter to keep track of unique menu IDs + menuIDCounter int64 + menuIDCounterMutex sync.Mutex +} + +func NewMenuItemMap() *MenuItemMap { + result := &MenuItemMap{ + idToMenuItemMap: make(map[string]*menu.MenuItem), + menuItemToIDMap: make(map[*menu.MenuItem]string), + } + + return result +} + +func (m *MenuItemMap) AddMenu(menu *menu.Menu) { + if menu == nil { + return + } + for _, item := range menu.Items { + m.processMenuItem(item) + } +} + +func (m *MenuItemMap) Dump() { + println("idToMenuItemMap:") + for key, value := range m.idToMenuItemMap { + fmt.Printf(" %s\t%p\n", key, value) + } + println("\nmenuItemToIDMap") + for key, value := range m.menuItemToIDMap { + fmt.Printf(" %p\t%s\n", key, value) + } +} + +// GenerateMenuID returns a unique string ID for a menu item +func (m *MenuItemMap) generateMenuID() string { + m.menuIDCounterMutex.Lock() + result := fmt.Sprintf("%d", m.menuIDCounter) + m.menuIDCounter++ + m.menuIDCounterMutex.Unlock() + return result +} + +func (m *MenuItemMap) processMenuItem(item *menu.MenuItem) { + + if item.SubMenu != nil { + for _, submenuitem := range item.SubMenu.Items { + m.processMenuItem(submenuitem) + } + } + + // Create a unique ID for this menu item + menuID := m.generateMenuID() + + // Store references + m.idToMenuItemMap[menuID] = item + m.menuItemToIDMap[item] = menuID +} + +func (m *MenuItemMap) getMenuItemByID(menuId string) *menu.MenuItem { + return m.idToMenuItemMap[menuId] +} diff --git a/v2/internal/menumanager/menumanager.go b/v2/internal/menumanager/menumanager.go new file mode 100644 index 000000000..ea7939415 --- /dev/null +++ b/v2/internal/menumanager/menumanager.go @@ -0,0 +1,116 @@ +package menumanager + +import ( + "fmt" + "github.com/wailsapp/wails/v2/pkg/menu" +) + +type Manager struct { + + // The application menu. + applicationMenu *menu.Menu + applicationMenuJSON string + processedApplicationMenu *WailsMenu + + // Our application menu mappings + applicationMenuItemMap *MenuItemMap + + // Context menus + contextMenus map[string]*ContextMenu + contextMenuPointers map[*menu.ContextMenu]string + + // Tray menu stores + trayMenus map[string]*TrayMenu + trayMenuPointers map[*menu.TrayMenu]string + + // Radio groups + radioGroups map[*menu.MenuItem][]*menu.MenuItem +} + +func NewManager() *Manager { + return &Manager{ + applicationMenuItemMap: NewMenuItemMap(), + contextMenus: make(map[string]*ContextMenu), + contextMenuPointers: make(map[*menu.ContextMenu]string), + trayMenus: make(map[string]*TrayMenu), + trayMenuPointers: make(map[*menu.TrayMenu]string), + radioGroups: make(map[*menu.MenuItem][]*menu.MenuItem), + } +} + +func (m *Manager) getMenuItemByID(menuMap *MenuItemMap, menuId string) *menu.MenuItem { + return menuMap.idToMenuItemMap[menuId] +} + +func (m *Manager) ProcessClick(menuID string, data string, menuType string, parentID string) error { + + var menuItemMap *MenuItemMap + + switch menuType { + case "ApplicationMenu": + menuItemMap = m.applicationMenuItemMap + case "ContextMenu": + contextMenu := m.contextMenus[parentID] + if contextMenu == nil { + return fmt.Errorf("unknown context menu: %s", parentID) + } + menuItemMap = contextMenu.menuItemMap + case "TrayMenu": + trayMenu := m.trayMenus[parentID] + if trayMenu == nil { + return fmt.Errorf("unknown tray menu: %s", parentID) + } + menuItemMap = trayMenu.menuItemMap + default: + return fmt.Errorf("unknown menutype: %s", menuType) + } + + // Get the menu item + menuItem := menuItemMap.getMenuItemByID(menuID) + if menuItem == nil { + return fmt.Errorf("Cannot process menuid %s - unknown", menuID) + } + + // Is the menu item a checkbox? + if menuItem.Type == menu.CheckboxType { + // Toggle state + menuItem.Checked = !menuItem.Checked + } + + if menuItem.Type == menu.RadioType { + println("Toggle radio") + // Get my radio group + for _, radioMenuItem := range m.radioGroups[menuItem] { + radioMenuItem.Checked = (radioMenuItem == menuItem) + } + } + + if menuItem.Click == nil { + // No callback + return fmt.Errorf("No callback for menu '%s'", menuItem.Label) + } + + // Create new Callback struct + callbackData := &menu.CallbackData{ + MenuItem: menuItem, + //ContextData: data, + } + + // Call back! + go menuItem.Click(callbackData) + + return nil +} + +func (m *Manager) processRadioGroups(processedMenu *WailsMenu, itemMap *MenuItemMap) { + for _, group := range processedMenu.RadioGroups { + radioGroupMenuItems := []*menu.MenuItem{} + for _, member := range group.Members { + item := m.getMenuItemByID(itemMap, member) + radioGroupMenuItems = append(radioGroupMenuItems, item) + } + for _, radioGroupMenuItem := range radioGroupMenuItems { + m.radioGroups[radioGroupMenuItem] = radioGroupMenuItems + } + } +} diff --git a/v2/internal/menumanager/processedMenu.go b/v2/internal/menumanager/processedMenu.go new file mode 100644 index 000000000..2722f86cd --- /dev/null +++ b/v2/internal/menumanager/processedMenu.go @@ -0,0 +1,189 @@ +package menumanager + +import ( + "encoding/json" + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/menu/keys" +) + +type ProcessedMenuItem struct { + ID string + // Label is what appears as the menu text + Label string `json:",omitempty"` + // Role is a predefined menu type + //Role menu.Role `json:",omitempty"` + // Accelerator holds a representation of a key binding + Accelerator *keys.Accelerator `json:",omitempty"` + // Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu + Type menu.Type + // Disabled makes the item unselectable + Disabled bool `json:",omitempty"` + // Hidden ensures that the item is not shown in the menu + Hidden bool `json:",omitempty"` + // Checked indicates if the item is selected (used by Checkbox and Radio types only) + Checked bool `json:",omitempty"` + // Submenu contains a list of menu items that will be shown as a submenu + //SubMenu []*MenuItem `json:"SubMenu,omitempty"` + SubMenu *ProcessedMenu `json:",omitempty"` + /* + // Colour + RGBA string `json:",omitempty"` + + // Font + FontSize int `json:",omitempty"` + FontName string `json:",omitempty"` + + // Image - base64 image data + Image string `json:",omitempty"` + MacTemplateImage bool `json:", omitempty"` + MacAlternate bool `json:", omitempty"` + + // Tooltip + Tooltip string `json:",omitempty"` + + // Styled label + StyledLabel []*ansi.StyledText `json:",omitempty"` + */ +} + +func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem { + + ID := menuItemMap.menuItemToIDMap[menuItem] + + // Parse ANSI text + //var styledLabel []*ansi.StyledText + //tempLabel := menuItem.Label + //if strings.Contains(tempLabel, "\033[") { + // parsedLabel, err := ansi.Parse(menuItem.Label) + // if err == nil { + // styledLabel = parsedLabel + // } + //} + + result := &ProcessedMenuItem{ + ID: ID, + Label: menuItem.Label, + //Role: menuItem.Role, + Accelerator: menuItem.Accelerator, + Type: menuItem.Type, + Disabled: menuItem.Disabled, + Hidden: menuItem.Hidden, + Checked: menuItem.Checked, + SubMenu: nil, + //RGBA: menuItem.RGBA, + //FontSize: menuItem.FontSize, + //FontName: menuItem.FontName, + //Image: menuItem.Image, + //MacTemplateImage: menuItem.MacTemplateImage, + //MacAlternate: menuItem.MacAlternate, + //Tooltip: menuItem.Tooltip, + //StyledLabel: styledLabel, + } + + if menuItem.SubMenu != nil { + result.SubMenu = NewProcessedMenu(menuItemMap, menuItem.SubMenu) + } + + return result +} + +type ProcessedMenu struct { + Items []*ProcessedMenuItem +} + +func NewProcessedMenu(menuItemMap *MenuItemMap, menu *menu.Menu) *ProcessedMenu { + + result := &ProcessedMenu{} + if menu != nil { + for _, item := range menu.Items { + processedMenuItem := NewProcessedMenuItem(menuItemMap, item) + result.Items = append(result.Items, processedMenuItem) + } + } + + return result +} + +// WailsMenu is the original menu with the addition +// of radio groups extracted from the menu data +type WailsMenu struct { + Menu *ProcessedMenu + RadioGroups []*RadioGroup + currentRadioGroup []string +} + +// RadioGroup holds all the members of the same radio group +type RadioGroup struct { + Members []string + Length int +} + +func NewWailsMenu(menuItemMap *MenuItemMap, menu *menu.Menu) *WailsMenu { + result := &WailsMenu{} + + // Process the menus + result.Menu = NewProcessedMenu(menuItemMap, menu) + + // Process the radio groups + result.processRadioGroups() + + return result +} + +func (w *WailsMenu) AsJSON() (string, error) { + + menuAsJSON, err := json.Marshal(w) + if err != nil { + return "", err + } + return string(menuAsJSON), nil +} + +func (w *WailsMenu) processRadioGroups() { + // Loop over top level menus + for _, item := range w.Menu.Items { + // Process MenuItem + w.processMenuItem(item) + } + + w.finaliseRadioGroup() +} + +func (w *WailsMenu) processMenuItem(item *ProcessedMenuItem) { + + switch item.Type { + + // We need to recurse submenus + case menu.SubmenuType: + + // Finalise any current radio groups as they don't trickle down to submenus + w.finaliseRadioGroup() + + // Process each submenu item + for _, subitem := range item.SubMenu.Items { + w.processMenuItem(subitem) + } + case menu.RadioType: + // Add the item to the radio group + w.currentRadioGroup = append(w.currentRadioGroup, item.ID) + default: + w.finaliseRadioGroup() + } +} + +func (w *WailsMenu) finaliseRadioGroup() { + + // If we were processing a radio group, fix up the references + if len(w.currentRadioGroup) > 0 { + + // Create new radiogroup + group := &RadioGroup{ + Members: w.currentRadioGroup, + Length: len(w.currentRadioGroup), + } + w.RadioGroups = append(w.RadioGroups, group) + + // Empty the radio group + w.currentRadioGroup = []string{} + } +} diff --git a/v2/internal/menumanager/traymenu.go b/v2/internal/menumanager/traymenu.go new file mode 100644 index 000000000..aed5b05ac --- /dev/null +++ b/v2/internal/menumanager/traymenu.go @@ -0,0 +1,222 @@ +package menumanager + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + "sync" + + "github.com/leaanthony/go-ansi-parser" + + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/pkg/menu" +) + +var trayMenuID int +var trayMenuIDMutex sync.Mutex + +func generateTrayID() string { + var idStr string + trayMenuIDMutex.Lock() + idStr = strconv.Itoa(trayMenuID) + trayMenuID++ + trayMenuIDMutex.Unlock() + return idStr +} + +type TrayMenu struct { + ID string + Label string + FontSize int + FontName string + Disabled bool + Tooltip string `json:",omitempty"` + Image string + MacTemplateImage bool + RGBA string + menuItemMap *MenuItemMap + menu *menu.Menu + ProcessedMenu *WailsMenu + trayMenu *menu.TrayMenu + StyledLabel []*ansi.StyledText `json:",omitempty"` +} + +func (t *TrayMenu) AsJSON() (string, error) { + data, err := json.Marshal(t) + if err != nil { + return "", err + } + return string(data), nil +} + +func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu { + + // Parse ANSI text + var styledLabel []*ansi.StyledText + tempLabel := trayMenu.Label + if strings.Contains(tempLabel, "\033[") { + parsedLabel, err := ansi.Parse(tempLabel) + if err == nil { + styledLabel = parsedLabel + } + } + + result := &TrayMenu{ + Label: trayMenu.Label, + FontName: trayMenu.FontName, + FontSize: trayMenu.FontSize, + Disabled: trayMenu.Disabled, + Tooltip: trayMenu.Tooltip, + Image: trayMenu.Image, + MacTemplateImage: trayMenu.MacTemplateImage, + menu: trayMenu.Menu, + RGBA: trayMenu.RGBA, + menuItemMap: NewMenuItemMap(), + trayMenu: trayMenu, + StyledLabel: styledLabel, + } + + result.menuItemMap.AddMenu(trayMenu.Menu) + result.ProcessedMenu = NewWailsMenu(result.menuItemMap, result.menu) + + return result +} + +func (m *Manager) OnTrayMenuOpen(id string) { + trayMenu, ok := m.trayMenus[id] + if !ok { + return + } + if trayMenu.trayMenu.OnOpen == nil { + return + } + go trayMenu.trayMenu.OnOpen() +} + +func (m *Manager) OnTrayMenuClose(id string) { + trayMenu, ok := m.trayMenus[id] + if !ok { + return + } + if trayMenu.trayMenu.OnClose == nil { + return + } + go trayMenu.trayMenu.OnClose() +} + +func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) { + newTrayMenu := NewTrayMenu(trayMenu) + + // Hook up a new ID + trayID := generateTrayID() + newTrayMenu.ID = trayID + + // Save the references + m.trayMenus[trayID] = newTrayMenu + m.trayMenuPointers[trayMenu] = trayID + + return newTrayMenu.AsJSON() +} + +func (m *Manager) GetTrayID(trayMenu *menu.TrayMenu) (string, error) { + trayID, exists := m.trayMenuPointers[trayMenu] + if !exists { + return "", fmt.Errorf("Unable to find menu ID for tray menu!") + } + return trayID, nil +} + +// SetTrayMenu updates or creates a menu +func (m *Manager) SetTrayMenu(trayMenu *menu.TrayMenu) (string, error) { + trayID, trayMenuKnown := m.trayMenuPointers[trayMenu] + if !trayMenuKnown { + return m.AddTrayMenu(trayMenu) + } + + // Create the updated tray menu + updatedTrayMenu := NewTrayMenu(trayMenu) + updatedTrayMenu.ID = trayID + + // Save the reference + m.trayMenus[trayID] = updatedTrayMenu + + return updatedTrayMenu.AsJSON() +} + +func (m *Manager) GetTrayMenus() ([]string, error) { + result := []string{} + for _, trayMenu := range m.trayMenus { + JSON, err := trayMenu.AsJSON() + if err != nil { + return nil, err + } + result = append(result, JSON) + } + + return result, nil +} + +func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) { + trayID, trayMenuKnown := m.trayMenuPointers[trayMenu] + if !trayMenuKnown { + return "", fmt.Errorf("[UpdateTrayMenuLabel] unknown tray id for tray %s", trayMenu.Label) + } + + type LabelUpdate struct { + ID string + Label string `json:",omitempty"` + FontName string `json:",omitempty"` + FontSize int + RGBA string `json:",omitempty"` + Disabled bool + Tooltip string `json:",omitempty"` + Image string `json:",omitempty"` + MacTemplateImage bool + StyledLabel []*ansi.StyledText `json:",omitempty"` + } + + // Parse ANSI text + var styledLabel []*ansi.StyledText + tempLabel := trayMenu.Label + if strings.Contains(tempLabel, "\033[") { + parsedLabel, err := ansi.Parse(tempLabel) + if err == nil { + styledLabel = parsedLabel + } + } + + update := &LabelUpdate{ + ID: trayID, + Label: trayMenu.Label, + FontName: trayMenu.FontName, + FontSize: trayMenu.FontSize, + Disabled: trayMenu.Disabled, + Tooltip: trayMenu.Tooltip, + Image: trayMenu.Image, + MacTemplateImage: trayMenu.MacTemplateImage, + RGBA: trayMenu.RGBA, + StyledLabel: styledLabel, + } + + data, err := json.Marshal(update) + if err != nil { + return "", errors.Wrap(err, "[UpdateTrayMenuLabel] ") + } + + return string(data), nil + +} + +func (m *Manager) GetContextMenus() ([]string, error) { + result := []string{} + for _, contextMenu := range m.contextMenus { + JSON, err := contextMenu.AsJSON() + if err != nil { + return nil, err + } + result = append(result, JSON) + } + + return result, nil +} diff --git a/v2/internal/messagedispatcher/dispatchclient.go b/v2/internal/messagedispatcher/dispatchclient.go new file mode 100644 index 000000000..af693660c --- /dev/null +++ b/v2/internal/messagedispatcher/dispatchclient.go @@ -0,0 +1,89 @@ +package messagedispatcher + +import ( + "fmt" + "github.com/wailsapp/wails/v2/pkg/runtime" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher/message" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// Client defines what a frontend client can do +type Client interface { + Quit() + NotifyEvent(message string) + CallResult(message string) + OpenFileDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) + OpenMultipleFilesDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) + OpenDirectoryDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) + SaveDialog(dialogOptions runtime.SaveDialogOptions, callbackID string) + MessageDialog(dialogOptions runtime.MessageDialogOptions, callbackID string) + WindowSetTitle(title string) + WindowShow() + WindowHide() + WindowCenter() + WindowMaximise() + WindowUnmaximise() + WindowMinimise() + WindowUnminimise() + WindowPosition(x int, y int) + WindowSize(width int, height int) + WindowSetMinSize(width int, height int) + WindowSetMaxSize(width int, height int) + WindowFullscreen() + WindowUnFullscreen() + WindowSetColour(colour int) + DarkModeEnabled(callbackID string) + SetApplicationMenu(menuJSON string) + SetTrayMenu(trayMenuJSON string) + UpdateTrayMenuLabel(JSON string) + UpdateContextMenu(contextMenuJSON string) + DeleteTrayMenuByID(id string) +} + +// DispatchClient is what the frontends use to interface with the +// dispatcher +type DispatchClient struct { + id string + logger logger.CustomLogger + + bus *servicebus.ServiceBus + + // Client + frontend Client +} + +func newDispatchClient(id string, frontend Client, logger logger.CustomLogger, bus *servicebus.ServiceBus) *DispatchClient { + + return &DispatchClient{ + id: id, + frontend: frontend, + logger: logger, + bus: bus, + } + +} + +// DispatchMessage is called by the front ends. It is passed +// an IPC message, translates it to a more concrete message +// type then publishes it on the service bus. +func (d *DispatchClient) DispatchMessage(incomingMessage string) { + + // Parse the message + d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage)) + parsedMessage, err := message.Parse(incomingMessage) + if err != nil { + d.logger.Error(err.Error()) + return + } + + // Save this client id + parsedMessage.ClientID = d.id + + d.logger.Trace("I got a parsedMessage: %+v", parsedMessage) + + // Publish the parsed message + d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id) + +} diff --git a/v2/internal/messagedispatcher/message/call.go b/v2/internal/messagedispatcher/message/call.go new file mode 100644 index 000000000..946e00a93 --- /dev/null +++ b/v2/internal/messagedispatcher/message/call.go @@ -0,0 +1,38 @@ +package message + +import ( + "encoding/json" + "fmt" +) + +type CallMessage struct { + Name string `json:"name"` + Args []json.RawMessage `json:"args"` + CallbackID string `json:"callbackID,omitempty"` +} + +// callMessageParser does what it says on the tin! +func callMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Call messages must be at least 3 bytes `C{}`` + if len(message) < 3 { + return nil, fmt.Errorf("call message was an invalid length") + } + + callMessage := new(CallMessage) + + m := message[1:] + + err := json.Unmarshal([]byte(m), callMessage) + if err != nil { + println(err.Error()) + return nil, err + } + + topic := "call:invoke" + + // Create a new parsed message struct + parsedMessage := &parsedMessage{Topic: topic, Data: callMessage} + + return parsedMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/contextmenus.go b/v2/internal/messagedispatcher/message/contextmenus.go new file mode 100644 index 000000000..53acebd0e --- /dev/null +++ b/v2/internal/messagedispatcher/message/contextmenus.go @@ -0,0 +1,43 @@ +package message + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/pkg/menu" +) + +// ContextMenusOnMessage is used to emit listener registration requests +// on the service bus +type ContextMenusOnMessage struct { + // MenuID is the id of the menu item we are interested in + MenuID string + // Callback is called when the menu is clicked + Callback func(*menu.MenuItem, string) +} + +// contextMenusMessageParser does what it says on the tin! +func contextMenusMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Menu messages must be at least 2 bytes + if len(message) < 3 { + return nil, fmt.Errorf("context menus message was an invalid length") + } + + var topic string + var data interface{} + + // Switch the message type + switch message[1] { + case 'C': + contextMenuData := message[2:] + topic = "contextmenus:clicked" + data = contextMenuData + default: + return nil, fmt.Errorf("invalid menu message: %s", message) + } + + // Create a new parsed message struct + parsedMessage := &parsedMessage{Topic: topic, Data: data} + + return parsedMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/dialog.go b/v2/internal/messagedispatcher/message/dialog.go new file mode 100644 index 000000000..5fcc63d30 --- /dev/null +++ b/v2/internal/messagedispatcher/message/dialog.go @@ -0,0 +1,61 @@ +package message + +import ( + "encoding/json" + "fmt" + "strings" +) + +// dialogMessageParser does what it says on the tin! +func dialogMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Dialog messages must be at least 4 bytes + if len(message) < 4 { + return nil, fmt.Errorf("dialog message was an invalid length") + } + + var topic = "bad topic from dialogMessageParser" + var responseMessage *parsedMessage + + // Switch the event type (with or without data) + switch message[0] { + // Format of Dialog response messages: D|<[]string as json encoded string> + case 'D': + dialogType := message[1] + message = message[2:] + idx := strings.IndexByte(message, '|') + if idx < 0 { + return nil, fmt.Errorf("Invalid dialog response message format: %+v", message) + } + callbackID := message[:idx] + payloadData := message[idx+1:] + + switch dialogType { + case 'O': + topic = "dialog:openselected:" + callbackID + responseMessage = &parsedMessage{Topic: topic, Data: payloadData} + case 'D': + topic = "dialog:opendirectoryselected:" + callbackID + responseMessage = &parsedMessage{Topic: topic, Data: payloadData} + case '*': + var data []string + topic = "dialog:openmultipleselected:" + callbackID + err := json.Unmarshal([]byte(payloadData), &data) + if err != nil { + return nil, err + } + responseMessage = &parsedMessage{Topic: topic, Data: data} + case 'S': + topic = "dialog:saveselected:" + callbackID + responseMessage = &parsedMessage{Topic: topic, Data: payloadData} + case 'M': + topic = "dialog:messageselected:" + callbackID + responseMessage = &parsedMessage{Topic: topic, Data: payloadData} + } + + default: + return nil, fmt.Errorf("Invalid message to dialogMessageParser()") + } + + return responseMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/event.go b/v2/internal/messagedispatcher/message/event.go new file mode 100644 index 000000000..dcb133e48 --- /dev/null +++ b/v2/internal/messagedispatcher/message/event.go @@ -0,0 +1,47 @@ +package message + +import ( + "encoding/json" + "fmt" +) + +type EventMessage struct { + Name string `json:"name"` + Data []interface{} `json:"data"` +} + +type OnEventMessage struct { + Name string + Callback func(optionalData ...interface{}) + Counter int +} + +// eventMessageParser does what it says on the tin! +func eventMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Event messages must be at least 2 bytes + if len(message) < 3 { + return nil, fmt.Errorf("event message was an invalid length") + } + + eventMessage := new(EventMessage) + direction := message[1] + + // Switch the event type (with or without data) + switch message[0] { + case 'E': + m := message[2:] + err := json.Unmarshal([]byte(m), eventMessage) + if err != nil { + println(err.Error()) + return nil, err + } + } + + topic := "event:emit:from:" + string(direction) + + // Create a new parsed message struct + parsedMessage := &parsedMessage{Topic: topic, Data: eventMessage} + + return parsedMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/log.go b/v2/internal/messagedispatcher/message/log.go new file mode 100644 index 000000000..9b24fc5a1 --- /dev/null +++ b/v2/internal/messagedispatcher/message/log.go @@ -0,0 +1,36 @@ +package message + +import "fmt" + +var logMessageMap = map[byte]string{ + 'P': "log:print", + 'T': "log:trace", + 'D': "log:debug", + 'I': "log:info", + 'W': "log:warning", + 'E': "log:error", + 'F': "log:fatal", + 'S': "log:setlevel", +} + +// logMessageParser does what it says on the tin! +func logMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Log messages must be at least 2 bytes + if len(message) < 2 { + return nil, fmt.Errorf("log message was an invalid length") + } + + // Switch on the log type + messageTopic := logMessageMap[message[1]] + + // If the type is invalid, raise error + if messageTopic == "" { + return nil, fmt.Errorf("log message type '%c' invalid", message[1]) + } + + // Create a new parsed message struct + parsedMessage := &parsedMessage{Topic: messageTopic, Data: message[2:]} + + return parsedMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/menu.go b/v2/internal/messagedispatcher/message/menu.go new file mode 100644 index 000000000..483865fb9 --- /dev/null +++ b/v2/internal/messagedispatcher/message/menu.go @@ -0,0 +1,51 @@ +package message + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/pkg/menu" +) + +// MenuOnMessage is used to emit listener registration requests +// on the service bus +type MenuOnMessage struct { + // MenuID is the id of the menu item we are interested in + MenuID string + // Callback is called when the menu is clicked + Callback func(*menu.MenuItem) +} + +// menuMessageParser does what it says on the tin! +func menuMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Menu messages must be at least 2 bytes + if len(message) < 3 { + return nil, fmt.Errorf("event message was an invalid length") + } + + var topic string + var data interface{} + + // Switch the message type + switch message[1] { + case 'C': + callbackid := message[2:] + topic = "menu:clicked" + data = callbackid + case 'o': + callbackid := message[2:] + topic = "menu:ontrayopen" + data = callbackid + case 'c': + callbackid := message[2:] + topic = "menu:ontrayclose" + data = callbackid + default: + return nil, fmt.Errorf("invalid menu message: %s", message) + } + + // Create a new parsed message struct + parsedMessage := &parsedMessage{Topic: topic, Data: data} + + return parsedMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/messageparser.go b/v2/internal/messagedispatcher/message/messageparser.go new file mode 100644 index 000000000..362070b0b --- /dev/null +++ b/v2/internal/messagedispatcher/message/messageparser.go @@ -0,0 +1,40 @@ +package message + +import "fmt" + +// Parse +type parsedMessage struct { + Topic string + ClientID string + Data interface{} +} + +// Map of different message parsers based on the header byte of the message +var messageParsers = map[byte]func(string) (*parsedMessage, error){ + 'L': logMessageParser, + 'R': runtimeMessageParser, + 'E': eventMessageParser, + 'C': callMessageParser, + 'W': windowMessageParser, + 'D': dialogMessageParser, + 'S': systemMessageParser, + 'M': menuMessageParser, + 'T': trayMessageParser, + 'X': contextMenusMessageParser, + 'U': urlMessageParser, +} + +// Parse will attempt to parse the given message +func Parse(message string) (*parsedMessage, error) { + + if len(message) == 0 { + return nil, fmt.Errorf("MessageParser received blank message") + } + + parseMethod := messageParsers[message[0]] + if parseMethod == nil { + return nil, fmt.Errorf("message type '%c' invalid", message[0]) + } + + return parseMethod(message) +} diff --git a/v2/internal/messagedispatcher/message/runtime.go b/v2/internal/messagedispatcher/message/runtime.go new file mode 100644 index 000000000..fc3a44258 --- /dev/null +++ b/v2/internal/messagedispatcher/message/runtime.go @@ -0,0 +1,37 @@ +package message + +import "fmt" + +// runtimeMessageParser does what it says on the tin! +func runtimeMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Log messages must be at least 2 bytes + if len(message) < 3 { + return nil, fmt.Errorf("runtime message was an invalid length") + } + + // Switch on the runtime module type + module := message[1] + switch module { + case 'B': + return processBrowserMessage(message) + } + + return nil, fmt.Errorf("unknown message: %s", message) +} + +// processBrowserMessage expects messages of the following format: +// RB +// O = Open +func processBrowserMessage(message string) (*parsedMessage, error) { + method := message[2] + switch method { + case 'O': + // Open URL + target := message[3:] + return &parsedMessage{Topic: "runtime:browser:open", Data: target}, nil + } + + return nil, fmt.Errorf("unknown browser message: %s", message) + +} diff --git a/v2/internal/messagedispatcher/message/system.go b/v2/internal/messagedispatcher/message/system.go new file mode 100644 index 000000000..1bda75140 --- /dev/null +++ b/v2/internal/messagedispatcher/message/system.go @@ -0,0 +1,50 @@ +package message + +import ( + "fmt" + "strings" +) + +// systemMessageParser does what it says on the tin! +func systemMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: system messages must be at least 2 bytes + if len(message) < 2 { + return nil, fmt.Errorf("system message was an invalid length") + } + + var responseMessage *parsedMessage + + // Remove 'S' + message = message[1:] + + // Switch the event type (with or without data) + switch message[0] { + // Format of system response messages: S| + // DarkModeEnabled + case 'D': + if len(message) < 4 { + return nil, fmt.Errorf("system message was an invalid length") + } + message = message[1:] + idx := strings.IndexByte(message, '|') + if idx < 0 { + return nil, fmt.Errorf("Invalid system response message format") + } + callbackID := message[:idx] + payloadData := message[idx+1:] + + topic := "systemresponse:" + callbackID + responseMessage = &parsedMessage{Topic: topic, Data: payloadData == "T"} + + // This is our startup hook - the frontend is now ready + case 'S': + topic := "hooks:startup" + startupURL := message[1:] + responseMessage = &parsedMessage{Topic: topic, Data: startupURL} + default: + return nil, fmt.Errorf("Invalid message to systemMessageParser()") + } + + return responseMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/tray.go b/v2/internal/messagedispatcher/message/tray.go new file mode 100644 index 000000000..ac1cde8ed --- /dev/null +++ b/v2/internal/messagedispatcher/message/tray.go @@ -0,0 +1,46 @@ +package message + +import ( + "fmt" + + "github.com/wailsapp/wails/v2/pkg/menu" +) + +// TrayOnMessage is used to emit listener registration requests +// on the service bus +type TrayOnMessage struct { + // MenuID is the id of the menu item we are interested in + MenuID string + // Callback is called when the menu is clicked + Callback func(*menu.MenuItem) +} + +// trayMessageParser does what it says on the tin! +func trayMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Menu messages must be at least 2 bytes + if len(message) < 3 { + return nil, fmt.Errorf("tray message was an invalid length") + } + + var topic string + var data interface{} + + // Switch the message type + switch message[1] { + case 'C': + callbackid := message[2:] + topic = "tray:clicked" + data = callbackid + case 'I': + topic = "trayfrontend:seticon" + data = message[2:] + default: + return nil, fmt.Errorf("invalid tray message: %s", message) + } + + // Create a new parsed message struct + parsedMessage := &parsedMessage{Topic: topic, Data: data} + + return parsedMessage, nil +} diff --git a/v2/internal/messagedispatcher/message/url.go b/v2/internal/messagedispatcher/message/url.go new file mode 100644 index 000000000..1bdc2f903 --- /dev/null +++ b/v2/internal/messagedispatcher/message/url.go @@ -0,0 +1,20 @@ +package message + +import "fmt" + +// urlMessageParser does what it says on the tin! +func urlMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: URL messages must be at least 2 bytes + if len(message) < 2 { + return nil, fmt.Errorf("log message was an invalid length") + } + + // Switch on the log type + switch message[1] { + case 'C': + return &parsedMessage{Topic: "url:handler", Data: message[2:]}, nil + default: + return nil, fmt.Errorf("url message type '%c' invalid", message[1]) + } +} diff --git a/v2/internal/messagedispatcher/message/window.go b/v2/internal/messagedispatcher/message/window.go new file mode 100644 index 000000000..e31353fac --- /dev/null +++ b/v2/internal/messagedispatcher/message/window.go @@ -0,0 +1,91 @@ +package message + +import "fmt" + +// windowMessageParser does what it says on the tin! +func windowMessageParser(message string) (*parsedMessage, error) { + + // Sanity check: Window messages must be at least 2 bytes + if len(message) < 2 { + return nil, fmt.Errorf("window message was an invalid length") + } + + // Extract event type + windowEvent := message[1] + parsedMessage := &parsedMessage{} + + // Switch the windowEvent type + switch windowEvent { + + // Closed window + case 'C': + parsedMessage.Topic = "quit" + parsedMessage.Data = "Window Closed" + + // Center window + case 'c': + parsedMessage.Topic = "window:center" + parsedMessage.Data = "" + + // Hide window + case 'H': + parsedMessage.Topic = "window:hide" + parsedMessage.Data = "" + + // Show window + case 'S': + parsedMessage.Topic = "window:show" + parsedMessage.Data = "" + + // Position window + case 'p': + parsedMessage.Topic = "window:position:" + message[3:] + parsedMessage.Data = "" + + // Set window size + case 's': + parsedMessage.Topic = "window:size:" + message[3:] + parsedMessage.Data = "" + + // Maximise window + case 'M': + parsedMessage.Topic = "window:maximise" + parsedMessage.Data = "" + + // Unmaximise window + case 'U': + parsedMessage.Topic = "window:unmaximise" + parsedMessage.Data = "" + + // Minimise window + case 'm': + parsedMessage.Topic = "window:minimise" + parsedMessage.Data = "" + + // Unminimise window + case 'u': + parsedMessage.Topic = "window:unminimise" + parsedMessage.Data = "" + + // Fullscreen window + case 'F': + parsedMessage.Topic = "window:fullscreen" + parsedMessage.Data = "" + + // UnFullscreen window + case 'f': + parsedMessage.Topic = "window:unfullscreen" + parsedMessage.Data = "" + + // Set Title + case 'T': + parsedMessage.Topic = "window:settitle" + parsedMessage.Data = message[2:] + + // Unknown event type + default: + return nil, fmt.Errorf("unknown message: %s", message) + } + + return parsedMessage, nil +} diff --git a/v2/internal/messagedispatcher/messagedispatcher.go b/v2/internal/messagedispatcher/messagedispatcher.go new file mode 100644 index 000000000..55828b0da --- /dev/null +++ b/v2/internal/messagedispatcher/messagedispatcher.go @@ -0,0 +1,577 @@ +package messagedispatcher + +import ( + "context" + "encoding/json" + "github.com/wailsapp/wails/v2/pkg/runtime" + "strconv" + "strings" + "sync" + + "github.com/wailsapp/wails/v2/internal/crypto" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher/message" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// Dispatcher translates messages received from the frontend +// and publishes them onto the service bus +type Dispatcher struct { + quitChannel <-chan *servicebus.Message + resultChannel <-chan *servicebus.Message + eventChannel <-chan *servicebus.Message + windowChannel <-chan *servicebus.Message + dialogChannel <-chan *servicebus.Message + systemChannel <-chan *servicebus.Message + menuChannel <-chan *servicebus.Message + + servicebus *servicebus.ServiceBus + logger logger.CustomLogger + + // Clients + clients map[string]*DispatchClient + lock sync.RWMutex + + // Context for cancellation + ctx context.Context + cancel context.CancelFunc + + // internal wait group + wg sync.WaitGroup +} + +// New dispatcher. Needs a service bus to send to. +func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher, error) { + // Subscribe to call result messages + resultChannel, err := servicebus.Subscribe("call:result") + if err != nil { + return nil, err + } + + // Subscribe to event messages + eventChannel, err := servicebus.Subscribe("event:emit") + if err != nil { + return nil, err + } + + // Subscribe to quit messages + quitChannel, err := servicebus.Subscribe("quit") + if err != nil { + return nil, err + } + + // Subscribe to window messages + windowChannel, err := servicebus.Subscribe("window") + if err != nil { + return nil, err + } + + // Subscribe to dialog events + dialogChannel, err := servicebus.Subscribe("dialog:select") + if err != nil { + return nil, err + } + + systemChannel, err := servicebus.Subscribe("system:") + if err != nil { + return nil, err + } + + menuChannel, err := servicebus.Subscribe("menufrontend:") + if err != nil { + return nil, err + } + + // Create context + ctx, cancel := context.WithCancel(context.Background()) + + result := &Dispatcher{ + servicebus: servicebus, + eventChannel: eventChannel, + logger: logger.CustomLogger("Message Dispatcher"), + clients: make(map[string]*DispatchClient), + resultChannel: resultChannel, + quitChannel: quitChannel, + windowChannel: windowChannel, + dialogChannel: dialogChannel, + systemChannel: systemChannel, + menuChannel: menuChannel, + ctx: ctx, + cancel: cancel, + } + + return result, nil +} + +// Start the subsystem +func (d *Dispatcher) Start() error { + + d.logger.Trace("Starting") + + d.wg.Add(1) + + // Spin off a go routine + go func() { + defer d.logger.Trace("Shutdown") + for { + select { + case <-d.ctx.Done(): + d.wg.Done() + return + case <-d.quitChannel: + d.processQuit() + case resultMessage := <-d.resultChannel: + d.processCallResult(resultMessage) + case eventMessage := <-d.eventChannel: + d.processEvent(eventMessage) + case windowMessage := <-d.windowChannel: + d.processWindowMessage(windowMessage) + case dialogMessage := <-d.dialogChannel: + d.processDialogMessage(dialogMessage) + case systemMessage := <-d.systemChannel: + d.processSystemMessage(systemMessage) + case menuMessage := <-d.menuChannel: + d.processMenuMessage(menuMessage) + } + } + }() + + return nil +} + +func (d *Dispatcher) processQuit() { + d.lock.RLock() + defer d.lock.RUnlock() + for _, client := range d.clients { + client.frontend.Quit() + } +} + +// RegisterClient will register the given callback with the dispatcher +// and return a DispatchClient that the caller can use to send messages +func (d *Dispatcher) RegisterClient(client Client) *DispatchClient { + d.lock.Lock() + defer d.lock.Unlock() + + // Create ID + id := d.getUniqueID() + d.clients[id] = newDispatchClient(id, client, d.logger, d.servicebus) + + return d.clients[id] +} + +// RemoveClient will remove the registered client +func (d *Dispatcher) RemoveClient(dc *DispatchClient) { + d.lock.Lock() + defer d.lock.Unlock() + delete(d.clients, dc.id) +} + +func (d *Dispatcher) getUniqueID() string { + var uid string + for { + uid = crypto.RandomID() + + if d.clients[uid] == nil { + break + } + } + + return uid +} + +func (d *Dispatcher) processCallResult(result *servicebus.Message) { + target := result.Target() + + if target == "" { + // This is an error. Calls are 1:1! + d.logger.Fatal("No target for call result: %+v", result) + } + + d.lock.RLock() + client := d.clients[target] + d.lock.RUnlock() + if client == nil { + // This is fatal - unknown target! + d.logger.Fatal("Unknown target for call result: %+v", result) + return + } + + d.logger.Trace("Sending message to client %s: R%s", target, result.Data().(string)) + client.frontend.CallResult(result.Data().(string)) +} + +// processSystem +func (d *Dispatcher) processSystemMessage(result *servicebus.Message) { + + d.logger.Trace("Got system in message dispatcher: %+v", result) + + splitTopic := strings.Split(result.Topic(), ":") + command := splitTopic[1] + callbackID := splitTopic[2] + switch command { + case "isdarkmode": + d.lock.RLock() + for _, client := range d.clients { + client.frontend.DarkModeEnabled(callbackID) + break + } + d.lock.RUnlock() + + default: + d.logger.Error("Unknown system command: %s", command) + } +} + +// processEvent will +func (d *Dispatcher) processEvent(result *servicebus.Message) { + + d.logger.Trace("Got event in message dispatcher: %+v", result) + + splitTopic := strings.Split(result.Topic(), ":") + eventType := splitTopic[1] + switch eventType { + case "emit": + eventFrom := splitTopic[3] + if eventFrom == "g" { + // This was sent from Go - notify frontend + eventData := result.Data().(*message.EventMessage) + // Unpack event + payload, err := json.Marshal(eventData) + if err != nil { + d.logger.Error("Unable to marshal eventData: %s", err.Error()) + return + } + d.lock.RLock() + for _, client := range d.clients { + client.frontend.NotifyEvent(string(payload)) + } + d.lock.RUnlock() + } + default: + d.logger.Error("Unknown event type: %s", eventType) + } +} + +// processWindowMessage processes messages intended for the window +func (d *Dispatcher) processWindowMessage(result *servicebus.Message) { + d.lock.RLock() + defer d.lock.RUnlock() + splitTopic := strings.Split(result.Topic(), ":") + command := splitTopic[1] + switch command { + case "settitle": + title, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid title for 'window:settitle' : %#v", result.Data()) + return + } + // Notify clients + for _, client := range d.clients { + client.frontend.WindowSetTitle(title) + } + case "fullscreen": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowFullscreen() + } + case "unfullscreen": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowUnFullscreen() + } + case "setcolour": + colour, ok := result.Data().(int) + if !ok { + d.logger.Error("Invalid colour for 'window:setcolour' : %#v", result.Data()) + return + } + // Notify clients + for _, client := range d.clients { + client.frontend.WindowSetColour(colour) + } + case "show": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowShow() + } + case "hide": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowHide() + } + case "center": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowCenter() + } + case "maximise": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowMaximise() + } + case "unmaximise": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowUnmaximise() + } + case "minimise": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowMinimise() + } + case "unminimise": + // Notify clients + for _, client := range d.clients { + client.frontend.WindowUnminimise() + } + case "position": + // We need 2 arguments + if len(splitTopic) != 4 { + d.logger.Error("Invalid number of parameters for 'window:position' : %#v", result.Data()) + return + } + x, err1 := strconv.Atoi(splitTopic[2]) + y, err2 := strconv.Atoi(splitTopic[3]) + if err1 != nil || err2 != nil { + d.logger.Error("Invalid integer parameters for 'window:position' : %#v", result.Data()) + return + } + // Notify clients + for _, client := range d.clients { + client.frontend.WindowPosition(x, y) + } + case "size": + // We need 2 arguments + if len(splitTopic) != 4 { + d.logger.Error("Invalid number of parameters for 'window:size' : %#v", result.Data()) + return + } + w, err1 := strconv.Atoi(splitTopic[2]) + h, err2 := strconv.Atoi(splitTopic[3]) + if err1 != nil || err2 != nil { + d.logger.Error("Invalid integer parameters for 'window:size' : %#v", result.Data()) + return + } + // Notifh clients + for _, client := range d.clients { + client.frontend.WindowSize(w, h) + } + case "minsize": + // We need 2 arguments + if len(splitTopic) != 4 { + d.logger.Error("Invalid number of parameters for 'window:minsize' : %#v", result.Data()) + return + } + w, err1 := strconv.Atoi(splitTopic[2]) + h, err2 := strconv.Atoi(splitTopic[3]) + if err1 != nil || err2 != nil { + d.logger.Error("Invalid integer parameters for 'window:minsize' : %#v", result.Data()) + return + } + // Notifh clients + for _, client := range d.clients { + client.frontend.WindowSetMinSize(w, h) + } + case "maxsize": + // We need 2 arguments + if len(splitTopic) != 4 { + d.logger.Error("Invalid number of parameters for 'window:maxsize' : %#v", result.Data()) + return + } + w, err1 := strconv.Atoi(splitTopic[2]) + h, err2 := strconv.Atoi(splitTopic[3]) + if err1 != nil || err2 != nil { + d.logger.Error("Invalid integer parameters for 'window:maxsize' : %#v", result.Data()) + return + } + // Notifh clients + for _, client := range d.clients { + client.frontend.WindowSetMaxSize(w, h) + } + default: + d.logger.Error("Unknown window command: %s", command) + } + d.logger.Trace("Got window in message dispatcher: %+v", result) + +} + +// processDialogMessage processes dialog messages +func (d *Dispatcher) processDialogMessage(result *servicebus.Message) { + splitTopic := strings.Split(result.Topic(), ":") + if len(splitTopic) < 4 { + d.logger.Error("Invalid dialog message : %#v", result.Data()) + return + } + + command := splitTopic[1] + switch command { + case "select": + dialogType := splitTopic[2] + switch dialogType { + case "open": + dialogOptions, ok := result.Data().(runtime.OpenDialogOptions) + if !ok { + d.logger.Error("Invalid data for 'dialog:select:open' : %#v", result.Data()) + return + } + // This is hardcoded in the sender too + callbackID := splitTopic[3] + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.OpenFileDialog(dialogOptions, callbackID) + } + case "openmultiple": + dialogOptions, ok := result.Data().(runtime.OpenDialogOptions) + if !ok { + d.logger.Error("Invalid data for 'dialog:select:openmultiple' : %#v", result.Data()) + return + } + // This is hardcoded in the sender too + callbackID := splitTopic[3] + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.OpenMultipleFilesDialog(dialogOptions, callbackID) + } + case "directory": + dialogOptions, ok := result.Data().(runtime.OpenDialogOptions) + if !ok { + d.logger.Error("Invalid data for 'dialog:select:directory' : %#v", result.Data()) + return + } + // This is hardcoded in the sender too + callbackID := splitTopic[3] + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.OpenDirectoryDialog(dialogOptions, callbackID) + } + case "save": + dialogOptions, ok := result.Data().(runtime.SaveDialogOptions) + if !ok { + d.logger.Error("Invalid data for 'dialog:select:save' : %#v", result.Data()) + return + } + // This is hardcoded in the sender too + callbackID := splitTopic[3] + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.SaveDialog(dialogOptions, callbackID) + } + case "message": + dialogOptions, ok := result.Data().(runtime.MessageDialogOptions) + if !ok { + d.logger.Error("Invalid data for 'dialog:select:message' : %#v", result.Data()) + return + } + // This is hardcoded in the sender too + callbackID := splitTopic[3] + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.MessageDialog(dialogOptions, callbackID) + } + default: + d.logger.Error("Unknown dialog type: %s", dialogType) + } + + default: + d.logger.Error("Unknown dialog command: %s", command) + } + +} + +func (d *Dispatcher) processMenuMessage(result *servicebus.Message) { + splitTopic := strings.Split(result.Topic(), ":") + if len(splitTopic) < 2 { + d.logger.Error("Invalid menu message : %#v", result.Data()) + return + } + + command := splitTopic[1] + switch command { + case "updateappmenu": + + updatedMenu, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid data for 'menufrontend:updateappmenu' : %#v", + result.Data()) + return + } + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.SetApplicationMenu(updatedMenu) + } + + case "settraymenu": + trayMenuJSON, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid data for 'menufrontend:settraymenu' : %#v", + result.Data()) + return + } + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.SetTrayMenu(trayMenuJSON) + } + + case "updatecontextmenu": + updatedContextMenu, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid data for 'menufrontend:updatecontextmenu' : %#v", + result.Data()) + return + } + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.UpdateContextMenu(updatedContextMenu) + } + + case "updatetraymenulabel": + updatedTrayMenuLabel, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#v", + result.Data()) + return + } + + // TODO: Work out what we mean in a multi window environment... + // For now we will just pick the first one + for _, client := range d.clients { + client.frontend.UpdateTrayMenuLabel(updatedTrayMenuLabel) + } + case "deletetraymenu": + traymenuid, ok := result.Data().(string) + if !ok { + d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#v", + result.Data()) + return + } + + for _, client := range d.clients { + client.frontend.DeleteTrayMenuByID(traymenuid) + } + + default: + d.logger.Error("Unknown menufrontend command: %s", command) + } +} + +func (d *Dispatcher) Close() { + d.cancel() + d.wg.Wait() +} diff --git a/v2/internal/parse/README.md b/v2/internal/parse/README.md new file mode 100644 index 000000000..5a40811a3 --- /dev/null +++ b/v2/internal/parse/README.md @@ -0,0 +1,10 @@ +# Parse + +Parse will attempt to parse your Wails project to perform a number of tasks: + * Verify that you have bound struct pointers + * Generate JS helper files/docs + +It currently checks bindings correctly if your code binds using one of the following methods: + * Literal Binding: `app.Bind(&MyStruct{})` + * Variable Binding: `app.Bind(m)` - m can be `m := &MyStruct{}` or `m := newMyStruct()` + * Function Binding: `app.Bind(newMyStruct())` diff --git a/v2/internal/parse/parse.go b/v2/internal/parse/parse.go new file mode 100644 index 000000000..0d897ff52 --- /dev/null +++ b/v2/internal/parse/parse.go @@ -0,0 +1,436 @@ +package main + +import ( + "fmt" + "go/ast" + "os" + "strings" + + "github.com/leaanthony/slicer" + "golang.org/x/tools/go/packages" +) + +var structCache = make(map[string]*ParsedStruct) +var boundStructs = make(map[string]*ParsedStruct) +var boundMethods = []string{} +var boundStructPointerLiterals = []string{} +var boundStructLiterals = slicer.StringSlicer{} +var boundVariables = slicer.StringSlicer{} +var app = "" +var structPointerFunctionDecls = make(map[string]string) +var structFunctionDecls = make(map[string]string) +var variableStructDecls = make(map[string]string) +var variableFunctionDecls = make(map[string]string) + +type Parameter struct { + Name string + Type string +} + +type ParsedMethod struct { + Struct string + Name string + Comments []string + Inputs []*Parameter + Returns []*Parameter +} + +type ParsedStruct struct { + Name string + Methods []*ParsedMethod +} + +type BoundStructs []*ParsedStruct + +func ParseProject(projectPath string) (BoundStructs, error) { + + cfg := &packages.Config{Mode: packages.NeedFiles | packages.NeedSyntax | packages.NeedTypesInfo} + pkgs, err := packages.Load(cfg, projectPath) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "load: %v\n", err) + os.Exit(1) + } + if packages.PrintErrors(pkgs) > 0 { + os.Exit(1) + } + + // Iterate the packages + for _, pkg := range pkgs { + + // Iterate the files + for _, file := range pkg.Syntax { + + var wailsPkgVar = "" + + ast.Inspect(file, func(n ast.Node) bool { + switch x := n.(type) { + // Parse import declarations + case *ast.ImportSpec: + // Determine what wails has been imported as + if x.Path.Value == `"github.com/wailsapp/wails/v2"` { + wailsPkgVar = x.Name.Name + } + // Parse calls. We are looking for app.Bind() calls + case *ast.CallExpr: + f, ok := x.Fun.(*ast.SelectorExpr) + if ok { + n, ok := f.X.(*ast.Ident) + if ok { + //Check this is the Bind() call associated with the app variable + if n.Name == app && f.Sel.Name == "Bind" { + if len(x.Args) == 1 { + ce, ok := x.Args[0].(*ast.CallExpr) + if ok { + n, ok := ce.Fun.(*ast.Ident) + if ok { + // We found a bind method using a function call + // EG: app.Bind( newMyStruct() ) + boundMethods = append(boundMethods, n.Name) + } + } else { + // We also want to check for Bind( &MyStruct{} ) + ue, ok := x.Args[0].(*ast.UnaryExpr) + if ok { + if ue.Op.String() == "&" { + cl, ok := ue.X.(*ast.CompositeLit) + if ok { + t, ok := cl.Type.(*ast.Ident) + if ok { + // We have found Bind( &MyStruct{} ) + boundStructPointerLiterals = append(boundStructPointerLiterals, t.Name) + } + } + } + } else { + // Let's check when the user binds a struct, + // rather than a struct pointer: Bind( MyStruct{} ) + // We do this to provide better hints to the user + cl, ok := x.Args[0].(*ast.CompositeLit) + if ok { + t, ok := cl.Type.(*ast.Ident) + if ok { + boundStructLiterals.Add(t.Name) + } + } else { + // Also check for when we bind a variable + // myVariable := &MyStruct{} + // app.Bind( myVariable ) + i, ok := x.Args[0].(*ast.Ident) + if ok { + boundVariables.Add(i.Name) + } + } + } + } + } + } + } + } + + // We scan assignments for a number of reasons: + // * Determine the variable containing the main application + // * Determine the type of variables that get used in Bind() + // * Determine the type of variables that get created with var := &MyStruct{} + case *ast.AssignStmt: + for _, rhs := range x.Rhs { + ce, ok := rhs.(*ast.CallExpr) + if ok { + se, ok := ce.Fun.(*ast.SelectorExpr) + if ok { + i, ok := se.X.(*ast.Ident) + if ok { + // Have we found the wails package name? + if i.Name == wailsPkgVar { + // Check we are calling a function to create the app + if se.Sel.Name == "CreateApp" || se.Sel.Name == "CreateAppWithOptions" { + if len(x.Lhs) == 1 { + i, ok := x.Lhs[0].(*ast.Ident) + if ok { + // Found the app variable name + app = i.Name + } + } + } + } + } + } else { + // Check for function assignment + // a := newMyStruct() + fe, ok := ce.Fun.(*ast.Ident) + if ok { + if len(x.Lhs) == 1 { + i, ok := x.Lhs[0].(*ast.Ident) + if ok { + // Store the variable -> Function mapping + // so we can later resolve the type + variableFunctionDecls[i.Name] = fe.Name + } + } + } + } + } else { + // Check for literal assignment of struct + // EG: myvar := MyStruct{} + ue, ok := rhs.(*ast.UnaryExpr) + if ok { + cl, ok := ue.X.(*ast.CompositeLit) + if ok { + t, ok := cl.Type.(*ast.Ident) + if ok { + if len(x.Lhs) == 1 { + i, ok := x.Lhs[0].(*ast.Ident) + if ok { + variableStructDecls[i.Name] = t.Name + } + } + } + } + } + } + } + // We scan for functions to build up a list of function names + // for a number of reasons: + // * Determine which functions are struct methods that are bound + // * Determine + case *ast.FuncDecl: + if x.Recv != nil { + // This is a struct method + for _, field := range x.Recv.List { + se, ok := field.Type.(*ast.StarExpr) + if ok { + // This is a struct pointer method + i, ok := se.X.(*ast.Ident) + if ok { + // If we haven't already found this struct, + // Create a placeholder in the cache + parsedStruct := structCache[i.Name] + if parsedStruct == nil { + structCache[i.Name] = &ParsedStruct{ + Name: i.Name, + } + parsedStruct = structCache[i.Name] + } + + // If this method is Public + if string(x.Name.Name[0]) == strings.ToUpper((string(x.Name.Name[0]))) { + structMethod := &ParsedMethod{ + Struct: i.Name, + Name: x.Name.Name, + } + // Check if the method has comments. + // If so, save it with the parsed method + if x.Doc != nil { + for _, comment := range x.Doc.List { + stringComment := comment.Text + if strings.HasPrefix(stringComment, "//") { + stringComment = stringComment[2:] + } + structMethod.Comments = append(structMethod.Comments, strings.TrimSpace(stringComment)) + } + } + + // Save the input parameters + for _, inputField := range x.Type.Params.List { + t, ok := inputField.Type.(*ast.Ident) + if !ok { + continue + } + for _, name := range inputField.Names { + structMethod.Inputs = append(structMethod.Inputs, &Parameter{Name: name.Name, Type: t.Name}) + } + } + + // Save the output parameters + for _, outputField := range x.Type.Results.List { + t, ok := outputField.Type.(*ast.Ident) + if !ok { + continue + } + if len(outputField.Names) == 0 { + structMethod.Returns = append(structMethod.Returns, &Parameter{Type: t.Name}) + } else { + for _, name := range outputField.Names { + structMethod.Returns = append(structMethod.Returns, &Parameter{Name: name.Name, Type: t.Name}) + } + } + } + + // Append this method to the parsed struct + parsedStruct.Methods = append(parsedStruct.Methods, structMethod) + + } + } + } + } + } else { + // This is a function declaration + // We care about its name and return type + // This will allow us to resolve types later + functionName := x.Name.Name + + // Look for one that returns a single value + if x.Type != nil && x.Type.Results != nil && x.Type.Results.List != nil { + if len(x.Type.Results.List) == 1 { + // Check for *struct + t, ok := x.Type.Results.List[0].Type.(*ast.StarExpr) + if ok { + s, ok := t.X.(*ast.Ident) + if ok { + // println("*** Function", functionName, "found which returns: *"+s.Name) + structPointerFunctionDecls[functionName] = s.Name + } + } else { + // Check for functions that return a struct + // This is to help us provide hints if the user binds a struct + t, ok := x.Type.Results.List[0].Type.(*ast.Ident) + if ok { + // println("*** Function", functionName, "found which returns: "+t.Name) + structFunctionDecls[functionName] = t.Name + } + } + } + } + } + } + return true + }) + // spew.Dump(file) + } + } + + /***** Update bound structs ******/ + + // Resolve bound Methods + for _, method := range boundMethods { + s, ok := structPointerFunctionDecls[method] + if !ok { + s, ok = structFunctionDecls[method] + if !ok { + println("Fatal: Bind statement using", method, "but cannot find", method, "declaration") + } else { + println("Fatal: Cannot bind struct using method `" + method + "` because it returns a struct (" + s + "). Return a pointer to " + s + " instead.") + } + os.Exit(1) + } + structDefinition := structCache[s] + if structDefinition == nil { + println("Fatal: Bind statement using `"+method+"` but cannot find struct", s, "definition") + os.Exit(1) + } + boundStructs[s] = structDefinition + } + + // Resolve bound vars + for _, structLiteral := range boundStructPointerLiterals { + s, ok := structCache[structLiteral] + if !ok { + println("Fatal: Bind statement using", structLiteral, "but cannot find", structLiteral, "declaration") + os.Exit(1) + } + boundStructs[structLiteral] = s + } + + // Resolve bound variables + boundVariables.Each(func(variable string) { + v, ok := variableStructDecls[variable] + if !ok { + method, ok := variableFunctionDecls[variable] + if !ok { + println("Fatal: Bind statement using variable `" + variable + "` which does not resolve to a struct pointer") + os.Exit(1) + } + + // Resolve function name + v, ok = structPointerFunctionDecls[method] + if !ok { + v, ok = structFunctionDecls[method] + if !ok { + println("Fatal: Bind statement using", method, "but cannot find", method, "declaration") + } else { + println("Fatal: Cannot bind variable `" + variable + "` because it resolves to a struct (" + v + "). Return a pointer to " + v + " instead.") + } + os.Exit(1) + } + + } + + s, ok := structCache[v] + if !ok { + println("Fatal: Bind statement using variable `" + variable + "` which resolves to a `" + v + "` but cannot find its declaration") + os.Exit(1) + } + boundStructs[v] = s + + }) + + // Check for struct literals + boundStructLiterals.Each(func(structName string) { + println("Fatal: Cannot bind struct using struct literal `" + structName + "{}`. Create a pointer to " + structName + " instead.") + os.Exit(1) + }) + + // Check for bound variables + // boundVariables.Each(func(varName string) { + // println("Fatal: Cannot bind struct using struct literal `" + structName + "{}`. Create a pointer to " + structName + " instead.") + // }) + + // spew.Dump(boundStructs) + // os.Exit(0) + + // } + // Inspect the AST and print all identifiers and literals. + + println("export {") + + noOfStructs := len(boundStructs) + structCount := 0 + for _, s := range boundStructs { + structCount++ + println() + println(" " + s.Name + ": {") + println() + noOfMethods := len(s.Methods) + for methodCount, m := range s.Methods { + println(" /****************") + for _, comment := range m.Comments { + println(" *", comment) + } + if len(m.Comments) > 0 { + println(" *") + } + inputNames := "" + for _, input := range m.Inputs { + println(" * @param {"+input.Type+"}", input.Name) + inputNames += input.Name + ", " + } + print(" * @return Promise<") + for _, output := range m.Returns { + print(output.Type + "|") + } + println("Error>") + println(" *") + println(" ***/") + if len(inputNames) > 2 { + inputNames = inputNames[:len(inputNames)-2] + } + println(" ", m.Name+": function("+inputNames+") {") + println(" return window.go." + s.Name + "." + m.Name + "(" + inputNames + ");") + print(" }") + if methodCount < noOfMethods-1 { + print(",") + } + println() + println() + } + print(" }") + if structCount < noOfStructs-1 { + print(",") + } + println() + } + println() + println("}") + println() + + return nil, nil +} diff --git a/v2/internal/process/process.go b/v2/internal/process/process.go new file mode 100644 index 000000000..6d497ed8e --- /dev/null +++ b/v2/internal/process/process.go @@ -0,0 +1,75 @@ +package process + +import ( + "os" + "os/exec" +) + +// Process defines a process that can be executed +type Process struct { + cmd *exec.Cmd + exitChannel chan bool + Running bool +} + +// NewProcess creates a new process struct +func NewProcess(cmd string, args ...string) *Process { + result := &Process{ + cmd: exec.Command(cmd, args...), + exitChannel: make(chan bool, 1), + } + result.cmd.Stdout = os.Stdout + result.cmd.Stderr = os.Stderr + return result +} + +// Start the process +func (p *Process) Start(exitCodeChannel chan int) error { + + err := p.cmd.Start() + if err != nil { + return err + } + + p.Running = true + + go func(cmd *exec.Cmd, running *bool, exitChannel chan bool, exitCodeChannel chan int) { + err := cmd.Wait() + if err == nil { + exitCodeChannel <- 0 + } + *running = false + exitChannel <- true + }(p.cmd, &p.Running, p.exitChannel, exitCodeChannel) + + return nil +} + +// Kill the process +func (p *Process) Kill() error { + if !p.Running { + return nil + } + err := p.cmd.Process.Kill() + if err != nil { + return err + } + err = p.cmd.Process.Release() + if err != nil { + return err + } + + // Wait for command to exit properly + <-p.exitChannel + + return err +} + +// PID returns the process PID +func (p *Process) PID() int { + return p.cmd.Process.Pid +} + +func (p *Process) SetDir(dir string) { + p.cmd.Dir = dir +} diff --git a/v2/internal/project/project.go b/v2/internal/project/project.go new file mode 100644 index 000000000..148f4eda6 --- /dev/null +++ b/v2/internal/project/project.go @@ -0,0 +1,115 @@ +package project + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" +) + +// Project holds the data related to a Wails project +type Project struct { + + /*** Application Data ***/ + Name string `json:"name"` + AssetDirectory string `json:"assetdir"` + + BuildCommand string `json:"frontend:build"` + InstallCommand string `json:"frontend:install"` + DevCommand string `json:"frontend:dev"` + + // Directory to generate the API Module + WailsJSDir string `json:"wailsjsdir"` + + Version string `json:"version"` + + /*** Internal Data ***/ + + // The path to the project directory + Path string + + // Build directory + BuildDir string + + // The output filename + OutputFilename string `json:"outputfilename"` + + // The type of application. EG: Desktop, Server, etc + OutputType string + + // The platform to target + Platform string + + // The application author + Author Author + + // Fully qualified filename + filename string + + // The debounce time for hot-reload of the built-in dev server. Default 100 + DebounceMS int `json:"debounceMS"` + + // The url to use to server assets. Default "https://localhost:34115" + DevServerURL string `json:"devserverurl"` +} + +func (p *Project) Save() error { + data, err := json.MarshalIndent(p, "", " ") + if err != nil { + return err + } + return os.WriteFile(p.filename, data, 0755) +} + +// Author stores details about the application author +type Author struct { + Name string `json:"name"` + Email string `json:"email"` +} + +// Load the project from the current working directory +func Load(projectPath string) (*Project, error) { + + // Attempt to load project.json + projectFile := filepath.Join(projectPath, "wails.json") + rawBytes, err := ioutil.ReadFile(projectFile) + if err != nil { + return nil, err + } + + // Unmarshal JSON + var result Project + err = json.Unmarshal(rawBytes, &result) + if err != nil { + return nil, err + } + + // Fix up our project paths + result.filename = projectFile + + if result.Version == "" { + result.Version = "2" + } + + // Create default name if not given + if result.Name == "" { + result.Name = "wailsapp" + } + + // Fix up OutputFilename + switch runtime.GOOS { + case "windows": + if !strings.HasSuffix(result.OutputFilename, ".exe") { + result.OutputFilename += ".exe" + } + case "darwin", "linux": + if strings.HasSuffix(result.OutputFilename, ".exe") { + result.OutputFilename = strings.TrimSuffix(result.OutputFilename, ".exe") + } + } + + // Return our project data + return &result, nil +} diff --git a/v2/internal/runtime/assets/assets.go b/v2/internal/runtime/assets/assets.go new file mode 100644 index 000000000..cdc0f3b2e --- /dev/null +++ b/v2/internal/runtime/assets/assets.go @@ -0,0 +1,12 @@ +package assets + +import _ "embed" + +//go:embed desktop_darwin.js +var desktopDarwinJS string + +//go:embed desktop_windows.js +var desktopWindowsJS string + +//go:embed wails.js +var wailsJS string diff --git a/v2/internal/runtime/assets/desktop_darwin.js b/v2/internal/runtime/assets/desktop_darwin.js new file mode 100644 index 000000000..4758b4e29 --- /dev/null +++ b/v2/internal/runtime/assets/desktop_darwin.js @@ -0,0 +1 @@ +var Wails=function(n){var t={};function e(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return n[r].call(i.exports,i,i.exports,e),i.l=!0,i.exports}return e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var i in n)e.d(r,i,function(t){return n[t]}.bind(null,i));return r},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=0)}([function(n,t,e){"use strict";e.r(t);var r={};e.r(r),e.d(r,"Trace",(function(){return v})),e.d(r,"Print",(function(){return p})),e.d(r,"Debug",(function(){return y})),e.d(r,"Info",(function(){return m})),e.d(r,"Warning",(function(){return b})),e.d(r,"Error",(function(){return g})),e.d(r,"Fatal",(function(){return h})),e.d(r,"SetLogLevel",(function(){return S})),e.d(r,"Level",(function(){return E}));var i={};e.r(i),e.d(i,"Open",(function(){return x}));var o={};e.r(o),e.d(o,"Center",(function(){return N})),e.d(o,"SetTitle",(function(){return T})),e.d(o,"Fullscreen",(function(){return j})),e.d(o,"UnFullscreen",(function(){return D})),e.d(o,"SetSize",(function(){return I})),e.d(o,"SetPosition",(function(){return P})),e.d(o,"Hide",(function(){return A})),e.d(o,"Show",(function(){return J})),e.d(o,"Maximise",(function(){return L})),e.d(o,"Unmaximise",(function(){return R})),e.d(o,"Minimise",(function(){return _})),e.d(o,"Unminimise",(function(){return F})),e.d(o,"Close",(function(){return U}));var a={};e.r(a),e.d(a,"Open",(function(){return B})),e.d(a,"Save",(function(){return H})),e.d(a,"Message",(function(){return G}));var u={};e.r(u),e.d(u,"New",(function(){return en}));var c={};e.r(c),e.d(c,"SetIcon",(function(){return rn}));var l={AppType:"desktop",Platform:function(){return"darwin"}};var s=[];function f(n){s.push(n)}function d(n){if(function(n){window.wailsInvoke(n)}(n),s.length>0)for(var t=0;t0)var a=setTimeout((function(){i(Error("Call to "+n+" timed out. Request ID: "+o))}),e);k[o]={timeoutHandle:a,reject:i,resolve:r};try{var u={name:n,args:t,callbackID:o};d("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function W(n){var t;try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw y(e),new Error(e)}var r=t.callbackid,i=k[r];if(!i){var o="Callback '".concat(r,"' not registered!!!");throw console.error(o),new Error(o)}clearTimeout(i.timeoutHandle),delete k[r],t.error?i.reject(t.error):i.resolve(t.result)}function M(n){var t=[].slice.apply(arguments).slice(1);return C(".wails."+n,t)}function x(n){return d("RBO"+n)}function N(){d("Wc")}function T(n){d("WT"+n)}function j(){d("WF")}function D(){d("Wf")}function I(n,t){d("Ws:"+n+":"+t)}function P(n,t){d("Wp:"+n+":"+t)}function A(){d("WH")}function J(){d("WS")}function L(){d("WM")}function R(){d("WU")}function _(){d("Wm")}function F(){d("Wu")}function U(){d("WC")}function B(n){return M("Dialog.Open",n)}function H(n){return M("Dialog.Save",n)}function G(n){return M("Dialog.Message",n)}O=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var q=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},z={};function V(n,t,e){z[n]=z[n]||[];var r=new q(t,e);console.log("Pushing event listener: "+n),z[n].push(r)}function K(n,t){V(n,t)}function Q(n,t){V(n,t,1)}function X(n){var t=n.name;if(z[t]){for(var e=z[t].slice(),r=0;r0)for(var t=0;t0)var a=setTimeout((function(){i(Error("Call to "+n+" timed out. Request ID: "+o))}),e);k[o]={timeoutHandle:a,reject:i,resolve:r};try{var u={name:n,args:t,callbackID:o};d("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function W(n){var t;try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw y(e),new Error(e)}var r=t.callbackid,i=k[r];if(!i){var o="Callback '".concat(r,"' not registered!!!");throw console.error(o),new Error(o)}clearTimeout(i.timeoutHandle),delete k[r],t.error?i.reject(t.error):i.resolve(t.result)}function x(n){var t=[].slice.apply(arguments).slice(1);return C(".wails."+n,t)}function M(n){return d("RBO"+n)}function N(){d("Wc")}function T(n){d("WT"+n)}function j(){d("WF")}function D(){d("Wf")}function I(n,t){d("Ws:"+n+":"+t)}function P(n,t){d("Wp:"+n+":"+t)}function A(){d("WH")}function J(){d("WS")}function L(){d("WM")}function R(){d("WU")}function _(){d("Wm")}function F(){d("Wu")}function U(){d("WC")}function B(n){return x("Dialog.Open",n)}function H(n){return x("Dialog.Save",n)}function G(n){return x("Dialog.Message",n)}O=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var q=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},z={};function V(n,t,e){z[n]=z[n]||[];var r=new q(t,e);console.log("Pushing event listener: "+n),z[n].push(r)}function K(n,t){V(n,t)}function Q(n,t){V(n,t,1)}function X(n){var t=n.name;if(z[t]){for(var e=z[t].slice(),r=0;r0)for(var t=0;t0)var a=setTimeout((function(){i(Error("Call to "+n+" timed out. Request ID: "+o))}),e);k[o]={timeoutHandle:a,reject:i,resolve:r};try{var u={name:n,args:t,callbackID:o};d("C"+JSON.stringify(u))}catch(n){console.error(n)}}))}function W(n){var t;try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw y(e),new Error(e)}var r=t.callbackid,i=k[r];if(!i){var o="Callback '".concat(r,"' not registered!!!");throw console.error(o),new Error(o)}clearTimeout(i.timeoutHandle),delete k[r],t.error?i.reject(t.error):i.resolve(t.result)}function N(n){var t=[].slice.apply(arguments).slice(1);return C(".wails."+n,t)}function T(n){return d("RBO"+n)}function j(){d("Wc")}function x(n){d("WT"+n)}function M(){d("WF")}function I(){d("Wf")}function D(n,t){d("Ws:"+n+":"+t)}function P(n,t){d("Wp:"+n+":"+t)}function A(){d("WH")}function J(){d("WS")}function L(){d("WM")}function R(){d("WU")}function _(){d("Wm")}function F(){d("Wu")}function U(){d("WC")}function B(n){return N("Dialog.Open",n)}function H(n){return N("Dialog.Save",n)}function G(n){return N("Dialog.Message",n)}O=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()},window.backend={};var q=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},z={};function V(n,t,e){z[n]=z[n]||[];var r=new q(t,e);console.log("Pushing event listener: "+n),z[n].push(r)}function K(n,t){V(n,t)}function Q(n,t){V(n,t,1)}function X(n){var t=n.name;if(z[t]){for(var e=z[t].slice(),r=0;r0)for(var e=0;e1){var e={name:n,data:[].slice.apply(arguments).slice(1)};a("Ej"+JSON.stringify(e))}else a("ej"+n)}var b={};var m,E={};function O(n,e,t){return null!=t&&null!=t||(t=0),new Promise((function(r,o){var i;do{i=n+"-"+m()}while(E[i]);if(t>0)var c=setTimeout((function(){o(Error("Call to "+n+" timed out. Request ID: "+i))}),t);E[i]={timeoutHandle:c,reject:o,resolve:r};try{JSON.stringify(e);a("call")}catch(n){console.error(n)}}))}function j(n){try{return new Function("var "+n),!0}catch(n){return!1}}function S(){return(S=Object.assign||function(n){for(var e=1;e1)for(var r=0;r + } + ], + outputs: [ + { + type: + } + ] + } + } + } +} + */ + +export function SetBindings(bindingsMap) { + try { + bindingsMap = JSON.parse(bindingsMap); + } catch (e) { + console.error(e); + } + + // Initialise the backend map + window.go = window.go || {}; + + // Iterate package names + Object.keys(bindingsMap).forEach((packageName) => { + + // Create inner map if it doesn't exist + window.go[packageName] = window.go[packageName] || {}; + + // Iterate struct names + Object.keys(bindingsMap[packageName]).forEach((structName) => { + + // Create inner map if it doesn't exist + window.go[packageName][structName] = window.go[packageName][structName] || {}; + + Object.keys(bindingsMap[packageName][structName]).forEach((methodName) => { + + window.go[packageName][structName][methodName] = function () { + + // No timeout by default + let timeout = 0; + + // Actual function + function dynamic() { + var args = [].slice.call(arguments); + return Call([packageName, structName, methodName].join('.'), args, timeout); + } + + // Allow setting timeout to function + dynamic.setTimeout = function (newTimeout) { + timeout = newTimeout; + }; + + // Allow getting timeout to function + dynamic.getTimeout = function () { + return timeout; + }; + + return dynamic; + }(); + }); + }); + }); +} diff --git a/v2/internal/runtime/js/core/calls.js b/v2/internal/runtime/js/core/calls.js new file mode 100644 index 000000000..242fbd8dc --- /dev/null +++ b/v2/internal/runtime/js/core/calls.js @@ -0,0 +1,157 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +import {Debug} from './log'; +import {SendMessage} from 'ipc'; + +var callbacks = {}; + +/** + * Returns a number from the native browser random function + * + * @returns number + */ +function cryptoRandom() { + var array = new Uint32Array(1); + return window.crypto.getRandomValues(array)[0]; +} + +/** + * Returns a number using da old-skool Math.Random + * I likes to call it LOLRandom + * + * @returns number + */ +function basicRandom() { + return Math.random() * 9007199254740991; +} + +// Pick a random number function based on browser capability +var randomFunc; +if (window.crypto) { + randomFunc = cryptoRandom; +} else { + randomFunc = basicRandom; +} + + +/** + * Call sends a message to the backend to call the binding with the + * given data. A promise is returned and will be completed when the + * backend responds. This will be resolved when the call was successful + * or rejected if an error is passed back. + * There is a timeout mechanism. If the call doesn't respond in the given + * time (in milliseconds) then the promise is rejected. + * + * @export + * @param {string} name + * @param {string} args + * @param {number=} timeout + * @returns + */ +export function Call(name, args, timeout) { + + // Timeout infinite by default + if (timeout == null || timeout == undefined) { + timeout = 0; + } + + // Create a promise + return new Promise(function (resolve, reject) { + + // Create a unique callbackID + var callbackID; + do { + callbackID = name + '-' + randomFunc(); + } while (callbacks[callbackID]); + + // Set timeout + if (timeout > 0) { + var timeoutHandle = setTimeout(function () { + reject(Error('Call to ' + name + ' timed out. Request ID: ' + callbackID)); + }, timeout); + } + + // Store callback + callbacks[callbackID] = { + timeoutHandle: timeoutHandle, + reject: reject, + resolve: resolve + }; + + try { + const payload = { + name, + args, + callbackID, + }; + + // Make the call + SendMessage('C' + JSON.stringify(payload)); + } catch (e) { + // eslint-disable-next-line + console.error(e); + } + }); +} + + + +/** + * Called by the backend to return data to a previously called + * binding invocation + * + * @export + * @param {string} incomingMessage + */ +export function Callback(incomingMessage) { + // Decode the message - Credit: https://stackoverflow.com/a/13865680 + //incomingMessage = decodeURIComponent(incomingMessage.replace(/\s+/g, '').replace(/[0-9a-f]{2}/g, '%$&')); + + // Parse the message + var message; + try { + message = JSON.parse(incomingMessage); + } catch (e) { + const error = `Invalid JSON passed to callback: ${e.message}. Message: ${incomingMessage}`; + Debug(error); + throw new Error(error); + } + var callbackID = message.callbackid; + var callbackData = callbacks[callbackID]; + if (!callbackData) { + const error = `Callback '${callbackID}' not registered!!!`; + console.error(error); // eslint-disable-line + throw new Error(error); + } + clearTimeout(callbackData.timeoutHandle); + + delete callbacks[callbackID]; + + if (message.error) { + callbackData.reject(message.error); + } else { + callbackData.resolve(message.result); + } +} + +/** + * SystemCall is used to call wails methods from the frontend + * + * @export + * @param {string} method + * @param {any[]=} data + * @returns + */ +export function SystemCall(method) { + var data = [].slice.apply(arguments).slice(1); + return Call('.wails.' + method, data); +} diff --git a/v2/internal/runtime/js/core/desktop.js b/v2/internal/runtime/js/core/desktop.js new file mode 100644 index 000000000..6649214e9 --- /dev/null +++ b/v2/internal/runtime/js/core/desktop.js @@ -0,0 +1,24 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ +import {SetBindings} from './bindings'; +import {Init} from './main'; + +// Initialise the Runtime +Init(); + +// Load Bindings if they exist +if (window.wailsbindings) { + SetBindings(window.wailsbindings); +} + +// Emit loaded event. Leaving this for now. It will show any errors if runtime fails to load. +window.wails.Events.Emit('wails:loaded'); + diff --git a/v2/internal/runtime/js/core/events.js b/v2/internal/runtime/js/core/events.js new file mode 100644 index 000000000..f9beebb10 --- /dev/null +++ b/v2/internal/runtime/js/core/events.js @@ -0,0 +1,169 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +import {Error} from './log'; +import {SendMessage} from 'ipc'; + +// Defines a single listener with a maximum number of times to callback + +/** + * The Listener class defines a listener! :-) + * + * @class Listener + */ +class Listener { + /** + * Creates an instance of Listener. + * @param {function} callback + * @param {number} maxCallbacks + * @memberof Listener + */ + constructor(callback, maxCallbacks) { + // Default of -1 means infinite + maxCallbacks = maxCallbacks || -1; + // Callback invokes the callback with the given data + // Returns true if this listener should be destroyed + this.Callback = (data) => { + callback.apply(null, data); + // If maxCallbacks is infinite, return false (do not destroy) + if (maxCallbacks === -1) { + return false; + } + // Decrement maxCallbacks. Return true if now 0, otherwise false + maxCallbacks -= 1; + return maxCallbacks === 0; + }; + } +} + +let eventListeners = {}; + +/** + * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + * @param {number} maxCallbacks + */ +export function OnMultiple(eventName, callback, maxCallbacks) { + eventListeners[eventName] = eventListeners[eventName] || []; + const thisListener = new Listener(callback, maxCallbacks); + console.log('Pushing event listener: ' + eventName); + eventListeners[eventName].push(thisListener); +} + +/** + * Registers an event listener that will be invoked every time the event is emitted + * + * @export + * @param {string} eventName + * @param {function} callback + */ +export function On(eventName, callback) { + OnMultiple(eventName, callback); +} + +/** + * Registers listeners for when the system theme changes from light/dark. A bool is + * sent to the listener, true if it is dark mode. + * + * @export + * @param {function} callback + */ +export function OnThemeChange(callback) { + On('wails:system:themechange', callback); +} + +/** + * Registers an event listener that will be invoked once then destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + */ +export function Once(eventName, callback) { + OnMultiple(eventName, callback, 1); +} + +function notifyListeners(eventData) { + + // Get the event name + let eventName = eventData.name; + + // Check if we have any listeners for this event + if (eventListeners[eventName]) { + + // Keep a list of listener indexes to destroy + const newEventListenerList = eventListeners[eventName].slice(); + + // Iterate listeners + for (let count = 0; count < eventListeners[eventName].length; count += 1) { + + // Get next listener + const listener = eventListeners[eventName][count]; + + let data = eventData.data; + + // Do the callback + const destroy = listener.Callback(data); + if (destroy) { + // if the listener indicated to destroy itself, add it to the destroy list + newEventListenerList.splice(count, 1); + } + } + + // Update callbacks with new list of listeners + eventListeners[eventName] = newEventListenerList; + } +} + +/** + * Notify informs frontend listeners that an event was emitted with the given data + * + * @export + * @param {string} notifyMessage - encoded notification message + + */ +export function Notify(notifyMessage) { + + // Parse the message + var message; + try { + message = JSON.parse(notifyMessage); + } catch (e) { + const error = 'Invalid JSON passed to Notify: ' + notifyMessage; + throw new Error(error); + } + + notifyListeners(message); +} + +/** + * Emit an event with the given name and data + * + * @export + * @param {string} eventName + */ +export function Emit(eventName) { + + const payload = { + name: eventName, + data: [].slice.apply(arguments).slice(1), + }; + + // Notify JS listeners + notifyListeners(payload); + + // Notify Go listeners + SendMessage('Ej' + JSON.stringify(payload)); + +} \ No newline at end of file diff --git a/v2/internal/runtime/js/core/log.js b/v2/internal/runtime/js/core/log.js new file mode 100644 index 000000000..ea624a8ee --- /dev/null +++ b/v2/internal/runtime/js/core/log.js @@ -0,0 +1,115 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + +import {SendMessage} from 'ipc'; + +/** + * Sends a log message to the backend with the given level + message + * + * @param {string} level + * @param {string} message + */ +function sendLogMessage(level, message) { + + // Log Message format: + // l[type][message] + SendMessage('L' + level + message); +} + +/** + * Log the given trace message with the backend + * + * @export + * @param {string} message + */ +export function Trace(message) { + sendLogMessage('T', message); +} + +/** + * Log the given message with the backend + * + * @export + * @param {string} message + */ +export function Print(message) { + sendLogMessage('P', message); +} + +/** + * Log the given debug message with the backend + * + * @export + * @param {string} message + */ +export function Debug(message) { + sendLogMessage('D', message); +} + +/** + * Log the given info message with the backend + * + * @export + * @param {string} message + */ +export function Info(message) { + sendLogMessage('I', message); +} + +/** + * Log the given warning message with the backend + * + * @export + * @param {string} message + */ +export function Warning(message) { + sendLogMessage('W', message); +} + +/** + * Log the given error message with the backend + * + * @export + * @param {string} message + */ +export function Error(message) { + sendLogMessage('E', message); +} + +/** + * Log the given fatal message with the backend + * + * @export + * @param {string} message + */ +export function Fatal(message) { + sendLogMessage('F', message); +} + +/** + * Sets the Log level to the given log level + * + * @export + * @param {number} loglevel + */ +export function SetLogLevel(loglevel) { + sendLogMessage('S', loglevel); +} + +// Log levels +export const Level = { + TRACE: 1, + DEBUG: 2, + INFO: 3, + WARNING: 4, + ERROR: 5, +}; diff --git a/v2/internal/runtime/js/core/main.js b/v2/internal/runtime/js/core/main.js new file mode 100644 index 000000000..3971cfc9c --- /dev/null +++ b/v2/internal/runtime/js/core/main.js @@ -0,0 +1,46 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ +import * as Log from './log'; +import {Emit, Notify, On, Once, OnMultiple} from './events'; +import {Callback, SystemCall} from './calls'; +import {AddScript, DisableDefaultContextMenu, InjectCSS} from './utils'; +import {AddIPCListener, SendMessage} from 'ipc'; +import * as Platform from 'platform'; + +export function Init() { + // Where the Go struct wrappers get bound to + window.go = {}; + + // Initialise global if not already + window.wails = { + Log, + Events: { + On, + Once, + OnMultiple, + Emit, + }, + _: { + Callback, + Notify, + AddScript, + InjectCSS, + DisableDefaultContextMenu, + // Init, + AddIPCListener, + SystemCall, + SendMessage, + }, + }; + + // Do platform specific Init + Platform.Init(); +} \ No newline at end of file diff --git a/v2/internal/runtime/js/core/utils.js b/v2/internal/runtime/js/core/utils.js new file mode 100644 index 000000000..34f376016 --- /dev/null +++ b/v2/internal/runtime/js/core/utils.js @@ -0,0 +1,42 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +import {Emit} from './events'; + +export function AddScript(js, callbackID) { + var script = document.createElement('script'); + script.text = js; + document.body.appendChild(script); + if (callbackID) { + Emit(callbackID); + } +} + +// Adapted from webview - thanks zserge! +export function InjectCSS(css) { + try { + 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); + } catch (e) { + console.log(e); + } +} + +export function DisableDefaultContextMenu() { + window.disableWailsDefaultContextMenu = true; +} diff --git a/v2/internal/runtime/js/desktop/common.js b/v2/internal/runtime/js/desktop/common.js new file mode 100644 index 000000000..80ed5408c --- /dev/null +++ b/v2/internal/runtime/js/desktop/common.js @@ -0,0 +1,15 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +/** + * Initialises platform specific code + */ +export const AppType = "desktop"; diff --git a/v2/internal/runtime/js/desktop/darwin.js b/v2/internal/runtime/js/desktop/darwin.js new file mode 100644 index 000000000..6b47f2f9b --- /dev/null +++ b/v2/internal/runtime/js/desktop/darwin.js @@ -0,0 +1,61 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + +/** + * Initialises platform specific code + */ + +export function SendMessage(message) { + window.wailsInvoke(message); +} + +export function Init() { + + // Setup drag handler + // Based on code from: https://github.com/patr0nus/DeskGap + window.addEventListener('mousedown', function (e) { + let currentElement = e.target; + while (currentElement != null) { + if (currentElement.hasAttribute('data-wails-no-drag')) { + break; + } else if (currentElement.hasAttribute('data-wails-drag')) { + window.wailsDrag(null); + break; + } + currentElement = currentElement.parentElement; + } + }); + + // Setup context menu hook + window.addEventListener('contextmenu', function (e) { + let currentElement = e.target; + let contextMenuId; + while (currentElement != null) { + contextMenuId = currentElement.dataset['wails-context-menu-id']; + if (contextMenuId != null) { + break; + } + currentElement = currentElement.parentElement; + } + if (contextMenuId != null || window.disableWailsDefaultContextMenu) { + e.preventDefault(); + } + if( contextMenuId != null ) { + let contextData = currentElement.dataset['wails-context-menu-data']; + let message = { + id: contextMenuId, + data: contextData || '', + }; + window.wailsContextMenuMessage(JSON.stringify(message)); + } + }); +} \ No newline at end of file diff --git a/v2/internal/runtime/js/desktop/ipc.js b/v2/internal/runtime/js/desktop/ipc.js new file mode 100644 index 000000000..b81a524eb --- /dev/null +++ b/v2/internal/runtime/js/desktop/ipc.js @@ -0,0 +1,41 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +import * as Platform from 'platform'; + +// IPC Listeners +var listeners = []; + +/** + * Adds a listener to IPC messages + * @param {function} callback + */ +export function AddIPCListener(callback) { + listeners.push(callback); +} + +/** + * SendMessage sends the given message to the backend + * + * @param {string} message + */ +export function SendMessage(message) { + + // Call Platform specific invoke method + Platform.SendMessage(message); + + // Also send to listeners + if (listeners.length > 0) { + for (var i = 0; i < listeners.length; i++) { + listeners[i](message); + } + } +} diff --git a/v2/internal/runtime/js/desktop/linux.js b/v2/internal/runtime/js/desktop/linux.js new file mode 100644 index 000000000..6b47f2f9b --- /dev/null +++ b/v2/internal/runtime/js/desktop/linux.js @@ -0,0 +1,61 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + +/** + * Initialises platform specific code + */ + +export function SendMessage(message) { + window.wailsInvoke(message); +} + +export function Init() { + + // Setup drag handler + // Based on code from: https://github.com/patr0nus/DeskGap + window.addEventListener('mousedown', function (e) { + let currentElement = e.target; + while (currentElement != null) { + if (currentElement.hasAttribute('data-wails-no-drag')) { + break; + } else if (currentElement.hasAttribute('data-wails-drag')) { + window.wailsDrag(null); + break; + } + currentElement = currentElement.parentElement; + } + }); + + // Setup context menu hook + window.addEventListener('contextmenu', function (e) { + let currentElement = e.target; + let contextMenuId; + while (currentElement != null) { + contextMenuId = currentElement.dataset['wails-context-menu-id']; + if (contextMenuId != null) { + break; + } + currentElement = currentElement.parentElement; + } + if (contextMenuId != null || window.disableWailsDefaultContextMenu) { + e.preventDefault(); + } + if( contextMenuId != null ) { + let contextData = currentElement.dataset['wails-context-menu-data']; + let message = { + id: contextMenuId, + data: contextData || '', + }; + window.wailsContextMenuMessage(JSON.stringify(message)); + } + }); +} \ No newline at end of file diff --git a/v2/internal/runtime/js/desktop/windows.js b/v2/internal/runtime/js/desktop/windows.js new file mode 100644 index 000000000..dd3b08f20 --- /dev/null +++ b/v2/internal/runtime/js/desktop/windows.js @@ -0,0 +1,61 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 9 */ + +/** + * Initialises platform specific code + */ + +export function SendMessage(message) { + window.wailsInvoke(message); +} + +export function Init() { + + // Setup drag handler + // Based on code from: https://github.com/patr0nus/DeskGap + window.addEventListener('mousedown', function (e) { + let currentElement = e.target; + while (currentElement != null) { + if (currentElement.hasAttribute('data-wails-no-drag')) { + break; + } else if (currentElement.hasAttribute('data-wails-drag')) { + window.wailsInvoke('wails-drag'); + break; + } + currentElement = currentElement.parentElement; + } + }); + + // Setup context menu hook + window.addEventListener('contextmenu', function (e) { + let currentElement = e.target; + let contextMenuId; + while (currentElement != null) { + contextMenuId = currentElement.dataset['wails-context-menu-id']; + if (contextMenuId != null) { + break; + } + currentElement = currentElement.parentElement; + } + if (contextMenuId != null || window.disableWailsDefaultContextMenu) { + e.preventDefault(); + } + if( contextMenuId != null ) { + let contextData = currentElement.dataset['wails-context-menu-data']; + let message = { + id: contextMenuId, + data: contextData || '', + }; + window.wailsInvoke('C'+JSON.stringify(message)); + } + }); +} \ No newline at end of file diff --git a/v2/internal/runtime/js/package-lock.json b/v2/internal/runtime/js/package-lock.json new file mode 100644 index 000000000..913320cea --- /dev/null +++ b/v2/internal/runtime/js/package-lock.json @@ -0,0 +1,5988 @@ +{ + "name": "wails-runtime", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/cli": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.8.4.tgz", + "integrity": "sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==", + "dev": true, + "requires": { + "chokidar": "^2.1.8", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.0.0", + "lodash": "^4.17.13", + "make-dir": "^2.1.0", + "slash": "^2.0.0", + "source-map": "^0.5.0" + } + }, + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/compat-data": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.9.0.tgz", + "integrity": "sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==", + "dev": true, + "requires": { + "browserslist": "^4.9.1", + "invariant": "^2.2.4", + "semver": "^5.5.0" + } + }, + "@babel/core": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.0.tgz", + "integrity": "sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.0", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helpers": "^7.9.0", + "@babel/parser": "^7.9.0", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz", + "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.9.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", + "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz", + "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.8.6", + "browserslist": "^4.9.1", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", + "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-regex": "^7.8.3", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", + "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/types": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", + "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-function-name": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", + "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.9.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", + "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-imports": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-module-transforms": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", + "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-simple-access": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.9.0", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", + "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-replace-supers": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.8.3", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/helper-simple-access": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", + "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.9.0", + "@babel/types": "^7.9.0" + } + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", + "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", + "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", + "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz", + "integrity": "sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz", + "integrity": "sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.9.5" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", + "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.8.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", + "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.8", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", + "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", + "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", + "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", + "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz", + "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-define-map": "^7.8.3", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-optimise-call-expression": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", + "@babel/helper-split-export-declaration": "^7.8.3", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", + "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz", + "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", + "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", + "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", + "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", + "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", + "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", + "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", + "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz", + "integrity": "sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz", + "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz", + "integrity": "sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz", + "integrity": "sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.9.0", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", + "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-object-assign": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.8.3.tgz", + "integrity": "sha512-i3LuN8tPDqUCRFu3dkzF2r1Nx0jp4scxtm7JxtIqI9he9Vk20YD+/zshdzR9JLsoBMlJlNR82a62vQExNEVx/Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz", + "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", + "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", + "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", + "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", + "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", + "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", + "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-regex": "^7.8.3" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", + "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", + "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", + "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/preset-env": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.9.5.tgz", + "integrity": "sha512-eWGYeADTlPJH+wq1F0wNfPbVS1w1wtmMJiYk55Td5Yu28AsdR9AsC97sZ0Qq8fHqQuslVSIYSGJMcblr345GfQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.9.0", + "@babel/helper-compilation-targets": "^7.8.7", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-numeric-separator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.9.5", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.9.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.9.5", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.9.5", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.9.0", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.9.0", + "@babel/plugin-transform-modules-commonjs": "^7.9.0", + "@babel/plugin-transform-modules-systemjs": "^7.9.0", + "@babel/plugin-transform-modules-umd": "^7.9.0", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.9.5", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.9.5", + "browserslist": "^4.9.1", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", + "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", + "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz", + "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.5", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz", + "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.5", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "ajv": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "babel-helper-evaluate-path": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.5.0.tgz", + "integrity": "sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==", + "dev": true + }, + "babel-helper-flip-expressions": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz", + "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=", + "dev": true + }, + "babel-helper-is-nodes-equiv": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz", + "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=", + "dev": true + }, + "babel-helper-is-void-0": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz", + "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=", + "dev": true + }, + "babel-helper-mark-eval-scopes": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz", + "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=", + "dev": true + }, + "babel-helper-remove-or-void": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz", + "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=", + "dev": true + }, + "babel-helper-to-multiple-sequence-expressions": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz", + "integrity": "sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA==", + "dev": true + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-minify-builtins": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.5.0.tgz", + "integrity": "sha512-wpqbN7Ov5hsNwGdzuzvFcjgRlzbIeVv1gMIlICbPj0xkexnfoIDe7q+AZHMkQmAE/F9R5jkrB6TLfTegImlXag==", + "dev": true + }, + "babel-plugin-minify-constant-folding": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.5.0.tgz", + "integrity": "sha512-Vj97CTn/lE9hR1D+jKUeHfNy+m1baNiJ1wJvoGyOBUx7F7kJqDZxr9nCHjO/Ad+irbR3HzR6jABpSSA29QsrXQ==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0" + } + }, + "babel-plugin-minify-dead-code-elimination": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.5.1.tgz", + "integrity": "sha512-x8OJOZIrRmQBcSqxBcLbMIK8uPmTvNWPXH2bh5MDCW1latEqYiRMuUkPImKcfpo59pTUB2FT7HfcgtG8ZlR5Qg==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-mark-eval-scopes": "^0.4.3", + "babel-helper-remove-or-void": "^0.4.3", + "lodash": "^4.17.11" + } + }, + "babel-plugin-minify-flip-comparisons": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz", + "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=", + "dev": true, + "requires": { + "babel-helper-is-void-0": "^0.4.3" + } + }, + "babel-plugin-minify-guarded-expressions": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.4.tgz", + "integrity": "sha512-RMv0tM72YuPPfLT9QLr3ix9nwUIq+sHT6z8Iu3sLbqldzC1Dls8DPCywzUIzkTx9Zh1hWX4q/m9BPoPed9GOfA==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-flip-expressions": "^0.4.3" + } + }, + "babel-plugin-minify-infinity": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz", + "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=", + "dev": true + }, + "babel-plugin-minify-mangle-names": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.5.0.tgz", + "integrity": "sha512-3jdNv6hCAw6fsX1p2wBGPfWuK69sfOjfd3zjUXkbq8McbohWy23tpXfy5RnToYWggvqzuMOwlId1PhyHOfgnGw==", + "dev": true, + "requires": { + "babel-helper-mark-eval-scopes": "^0.4.3" + } + }, + "babel-plugin-minify-numeric-literals": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz", + "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=", + "dev": true + }, + "babel-plugin-minify-replace": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.5.0.tgz", + "integrity": "sha512-aXZiaqWDNUbyNNNpWs/8NyST+oU7QTpK7J9zFEFSA0eOmtUNMU3fczlTTTlnCxHmq/jYNFEmkkSG3DDBtW3Y4Q==", + "dev": true + }, + "babel-plugin-minify-simplify": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.5.1.tgz", + "integrity": "sha512-OSYDSnoCxP2cYDMk9gxNAed6uJDiDz65zgL6h8d3tm8qXIagWGMLWhqysT6DY3Vs7Fgq7YUDcjOomhVUb+xX6A==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-flip-expressions": "^0.4.3", + "babel-helper-is-nodes-equiv": "^0.0.1", + "babel-helper-to-multiple-sequence-expressions": "^0.5.0" + } + }, + "babel-plugin-minify-type-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz", + "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=", + "dev": true, + "requires": { + "babel-helper-is-void-0": "^0.4.3" + } + }, + "babel-plugin-transform-inline-consecutive-adds": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz", + "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=", + "dev": true + }, + "babel-plugin-transform-member-expression-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz", + "integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=", + "dev": true + }, + "babel-plugin-transform-merge-sibling-variables": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz", + "integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=", + "dev": true + }, + "babel-plugin-transform-minify-booleans": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz", + "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=", + "dev": true + }, + "babel-plugin-transform-property-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz", + "integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "babel-plugin-transform-regexp-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz", + "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=", + "dev": true + }, + "babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=", + "dev": true + }, + "babel-plugin-transform-remove-debugger": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz", + "integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=", + "dev": true + }, + "babel-plugin-transform-remove-undefined": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.5.0.tgz", + "integrity": "sha512-+M7fJYFaEE/M9CXa0/IRkDbiV3wRELzA1kKQFCJ4ifhrzLKn/9VCCgj9OFmYWwBd8IB48YdgPkHYtbYq+4vtHQ==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0" + } + }, + "babel-plugin-transform-simplify-comparison-operators": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", + "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=", + "dev": true + }, + "babel-plugin-transform-undefined-to-void": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz", + "integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=", + "dev": true + }, + "babel-preset-minify": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.5.1.tgz", + "integrity": "sha512-1IajDumYOAPYImkHbrKeiN5AKKP9iOmRoO2IPbIuVp0j2iuCcj0n7P260z38siKMZZ+85d3mJZdtW8IgOv+Tzg==", + "dev": true, + "requires": { + "babel-plugin-minify-builtins": "^0.5.0", + "babel-plugin-minify-constant-folding": "^0.5.0", + "babel-plugin-minify-dead-code-elimination": "^0.5.1", + "babel-plugin-minify-flip-comparisons": "^0.4.3", + "babel-plugin-minify-guarded-expressions": "^0.4.4", + "babel-plugin-minify-infinity": "^0.4.3", + "babel-plugin-minify-mangle-names": "^0.5.0", + "babel-plugin-minify-numeric-literals": "^0.4.3", + "babel-plugin-minify-replace": "^0.5.0", + "babel-plugin-minify-simplify": "^0.5.1", + "babel-plugin-minify-type-constructors": "^0.4.3", + "babel-plugin-transform-inline-consecutive-adds": "^0.4.3", + "babel-plugin-transform-member-expression-literals": "^6.9.4", + "babel-plugin-transform-merge-sibling-variables": "^6.9.4", + "babel-plugin-transform-minify-booleans": "^6.9.4", + "babel-plugin-transform-property-literals": "^6.9.4", + "babel-plugin-transform-regexp-constructors": "^0.4.3", + "babel-plugin-transform-remove-console": "^6.9.4", + "babel-plugin-transform-remove-debugger": "^6.9.4", + "babel-plugin-transform-remove-undefined": "^0.5.0", + "babel-plugin-transform-simplify-comparison-operators": "^6.9.4", + "babel-plugin-transform-undefined-to-void": "^6.9.4", + "lodash": "^4.17.11" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz", + "integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001038", + "electron-to-chromium": "^1.3.390", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001171", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001171.tgz", + "integrity": "sha512-5Alrh8TTYPG9IH4UkRqEBZoEToWRLvPbSQokvzSz0lii8/FOWKG4keO1HoYfPWs8IF/NH/dyNPg1cmJGvV3Zlg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "dev": true + }, + "core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "dev": true, + "requires": { + "browserslist": "^4.8.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "electron-to-chromium": { + "version": "1.3.402", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.402.tgz", + "integrity": "sha512-gaCDfX7IUH0s3JmBiHCDPrvVcdnTTP1r4WLJc2dHkYYbLmXZ2XHiJCcGQ9Balf91aKTvuCKCyu2JjJYRykoI1w==", + "dev": true + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.2.0.tgz", + "integrity": "sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==", + "dev": true, + "requires": { + "estraverse": "^5.0.0" + }, + "dependencies": { + "estraverse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", + "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "1.1.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.53.tgz", + "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + } + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", + "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", + "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "terser": { + "version": "4.6.11", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.11.tgz", + "integrity": "sha512-76Ynm7OXUG5xhOpblhytE7X58oeNSmC8xnNhjWVo8CksHit0U0kO4hfNbPrrYwowLWFgM2n9L176VNx2QaHmtA==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "watchpack": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", + "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", + "dev": true, + "requires": { + "chokidar": "^2.1.8", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "webpack": { + "version": "4.42.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz", + "integrity": "sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-cli": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", + "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==", + "dev": true, + "requires": { + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" + }, + "dependencies": { + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "v8-compile-cache": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", + "dev": true + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/v2/internal/runtime/js/package.json b/v2/internal/runtime/js/package.json new file mode 100644 index 000000000..1c4d079f4 --- /dev/null +++ b/v2/internal/runtime/js/package.json @@ -0,0 +1,44 @@ +{ + "name": "wails-runtime", + "version": "1.0.0", + "description": "The Javascript Wails Runtime", + "main": "index.js", + "scripts": { + "build:desktop": "npx eslint core/ && npx webpack --env desktop --colors", + "build:server": "npx eslint core/ && npx webpack --env server --colors", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wailsapp/runtime.git" + }, + "keywords": [ + "Wails", + "Go", + "Javascript", + "Runtime" + ], + "browserslist": [ + "> 5%", + "IE 9" + ], + "author": "Lea Anthony ", + "license": "MIT", + "bugs": { + "url": "https://github.com/wailsapp/runtime/issues" + }, + "homepage": "https://github.com/wailsapp/runtime#readme", + "devDependencies": { + "@babel/cli": "^7.7.7", + "@babel/core": "^7.7.7", + "@babel/plugin-transform-object-assign": "^7.7.4", + "@babel/preset-env": "^7.7.7", + "babel-loader": "^8.0.6", + "babel-preset-minify": "^0.5.1", + "core-js": "^3.6.1", + "eslint": "^6.8.0", + "webpack": "^4.41.5", + "webpack-cli": "^3.3.10" + }, + "dependencies": {} +} diff --git a/v2/internal/runtime/js/package.json.md5 b/v2/internal/runtime/js/package.json.md5 new file mode 100644 index 000000000..c565f69d7 --- /dev/null +++ b/v2/internal/runtime/js/package.json.md5 @@ -0,0 +1 @@ +7a2c438e79cf603ba763055e515650be \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/.npmignore b/v2/internal/runtime/js/runtime/.npmignore new file mode 100644 index 000000000..e8310385c --- /dev/null +++ b/v2/internal/runtime/js/runtime/.npmignore @@ -0,0 +1 @@ +src \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/README.md b/v2/internal/runtime/js/runtime/README.md new file mode 100644 index 000000000..1859d4023 --- /dev/null +++ b/v2/internal/runtime/js/runtime/README.md @@ -0,0 +1,3 @@ +# Wails Runtime + +This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) V2 project, not a standalone module. diff --git a/v2/internal/runtime/js/runtime/bridge.js b/v2/internal/runtime/js/runtime/bridge.js new file mode 100644 index 000000000..a58076a98 --- /dev/null +++ b/v2/internal/runtime/js/runtime/bridge.js @@ -0,0 +1,1726 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.bridge = {})); +}(this, (function (exports) { 'use strict'; + + function noop() { } + const identity = x => x; + function run(fn) { + return fn(); + } + function blank_object() { + return Object.create(null); + } + function run_all(fns) { + fns.forEach(run); + } + function is_function(thing) { + return typeof thing === 'function'; + } + function safe_not_equal(a, b) { + return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); + } + function is_empty(obj) { + return Object.keys(obj).length === 0; + } + function subscribe(store, ...callbacks) { + if (store == null) { + return noop; + } + const unsub = store.subscribe(...callbacks); + return unsub.unsubscribe ? () => unsub.unsubscribe() : unsub; + } + function component_subscribe(component, store, callback) { + component.$$.on_destroy.push(subscribe(store, callback)); + } + function action_destroyer(action_result) { + return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; + } + + const is_client = typeof window !== 'undefined'; + let now = is_client + ? () => window.performance.now() + : () => Date.now(); + let raf = is_client ? cb => requestAnimationFrame(cb) : noop; + + const tasks = new Set(); + function run_tasks(now) { + tasks.forEach(task => { + if (!task.c(now)) { + tasks.delete(task); + task.f(); + } + }); + if (tasks.size !== 0) + raf(run_tasks); + } + /** + * Creates a new task that runs on each raf frame + * until it returns a falsy value or is aborted + */ + function loop(callback) { + let task; + if (tasks.size === 0) + raf(run_tasks); + return { + promise: new Promise(fulfill => { + tasks.add(task = { c: callback, f: fulfill }); + }), + abort() { + tasks.delete(task); + } + }; + } + + function append(target, node) { + target.appendChild(node); + } + function insert(target, node, anchor) { + target.insertBefore(node, anchor || null); + } + function detach(node) { + node.parentNode.removeChild(node); + } + function destroy_each(iterations, detaching) { + for (let i = 0; i < iterations.length; i += 1) { + if (iterations[i]) + iterations[i].d(detaching); + } + } + function element(name) { + return document.createElement(name); + } + function text(data) { + return document.createTextNode(data); + } + function space() { + return text(' '); + } + function empty() { + return text(''); + } + function listen(node, event, handler, options) { + node.addEventListener(event, handler, options); + return () => node.removeEventListener(event, handler, options); + } + function attr(node, attribute, value) { + if (value == null) + node.removeAttribute(attribute); + else if (node.getAttribute(attribute) !== value) + node.setAttribute(attribute, value); + } + function children(element) { + return Array.from(element.childNodes); + } + function set_data(text, data) { + data = '' + data; + if (text.wholeText !== data) + text.data = data; + } + function custom_event(type, detail) { + const e = document.createEvent('CustomEvent'); + e.initCustomEvent(type, false, false, detail); + return e; + } + + const active_docs = new Set(); + let active = 0; + // https://github.com/darkskyapp/string-hash/blob/master/index.js + function hash(str) { + let hash = 5381; + let i = str.length; + while (i--) + hash = ((hash << 5) - hash) ^ str.charCodeAt(i); + return hash >>> 0; + } + function create_rule(node, a, b, duration, delay, ease, fn, uid = 0) { + const step = 16.666 / duration; + let keyframes = '{\n'; + for (let p = 0; p <= 1; p += step) { + const t = a + (b - a) * ease(p); + keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`; + } + const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; + const name = `__svelte_${hash(rule)}_${uid}`; + const doc = node.ownerDocument; + active_docs.add(doc); + const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style')).sheet); + const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); + if (!current_rules[name]) { + current_rules[name] = true; + stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); + } + const animation = node.style.animation || ''; + node.style.animation = `${animation ? `${animation}, ` : ''}${name} ${duration}ms linear ${delay}ms 1 both`; + active += 1; + return name; + } + function delete_rule(node, name) { + const previous = (node.style.animation || '').split(', '); + const next = previous.filter(name + ? anim => anim.indexOf(name) < 0 // remove specific animation + : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations + ); + const deleted = previous.length - next.length; + if (deleted) { + node.style.animation = next.join(', '); + active -= deleted; + if (!active) + clear_rules(); + } + } + function clear_rules() { + raf(() => { + if (active) + return; + active_docs.forEach(doc => { + const stylesheet = doc.__svelte_stylesheet; + let i = stylesheet.cssRules.length; + while (i--) + stylesheet.deleteRule(i); + doc.__svelte_rules = {}; + }); + active_docs.clear(); + }); + } + + let current_component; + function set_current_component(component) { + current_component = component; + } + function get_current_component() { + if (!current_component) + throw new Error('Function called outside component initialization'); + return current_component; + } + function onMount(fn) { + get_current_component().$$.on_mount.push(fn); + } + + const dirty_components = []; + const binding_callbacks = []; + const render_callbacks = []; + const flush_callbacks = []; + const resolved_promise = Promise.resolve(); + let update_scheduled = false; + function schedule_update() { + if (!update_scheduled) { + update_scheduled = true; + resolved_promise.then(flush); + } + } + function add_render_callback(fn) { + render_callbacks.push(fn); + } + let flushing = false; + const seen_callbacks = new Set(); + function flush() { + if (flushing) + return; + flushing = true; + do { + // first, call beforeUpdate functions + // and update components + for (let i = 0; i < dirty_components.length; i += 1) { + const component = dirty_components[i]; + set_current_component(component); + update(component.$$); + } + set_current_component(null); + dirty_components.length = 0; + while (binding_callbacks.length) + binding_callbacks.pop()(); + // then, once components are updated, call + // afterUpdate functions. This may cause + // subsequent updates... + for (let i = 0; i < render_callbacks.length; i += 1) { + const callback = render_callbacks[i]; + if (!seen_callbacks.has(callback)) { + // ...so guard against infinite loops + seen_callbacks.add(callback); + callback(); + } + } + render_callbacks.length = 0; + } while (dirty_components.length); + while (flush_callbacks.length) { + flush_callbacks.pop()(); + } + update_scheduled = false; + flushing = false; + seen_callbacks.clear(); + } + function update($$) { + if ($$.fragment !== null) { + $$.update(); + run_all($$.before_update); + const dirty = $$.dirty; + $$.dirty = [-1]; + $$.fragment && $$.fragment.p($$.ctx, dirty); + $$.after_update.forEach(add_render_callback); + } + } + + let promise; + function wait() { + if (!promise) { + promise = Promise.resolve(); + promise.then(() => { + promise = null; + }); + } + return promise; + } + function dispatch(node, direction, kind) { + node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`)); + } + const outroing = new Set(); + let outros; + function group_outros() { + outros = { + r: 0, + c: [], + p: outros // parent group + }; + } + function check_outros() { + if (!outros.r) { + run_all(outros.c); + } + outros = outros.p; + } + function transition_in(block, local) { + if (block && block.i) { + outroing.delete(block); + block.i(local); + } + } + function transition_out(block, local, detach, callback) { + if (block && block.o) { + if (outroing.has(block)) + return; + outroing.add(block); + outros.c.push(() => { + outroing.delete(block); + if (callback) { + if (detach) + block.d(1); + callback(); + } + }); + block.o(local); + } + } + const null_transition = { duration: 0 }; + function create_bidirectional_transition(node, fn, params, intro) { + let config = fn(node, params); + let t = intro ? 0 : 1; + let running_program = null; + let pending_program = null; + let animation_name = null; + function clear_animation() { + if (animation_name) + delete_rule(node, animation_name); + } + function init(program, duration) { + const d = program.b - t; + duration *= Math.abs(d); + return { + a: t, + b: program.b, + d, + duration, + start: program.start, + end: program.start + duration, + group: program.group + }; + } + function go(b) { + const { delay = 0, duration = 300, easing = identity, tick = noop, css } = config || null_transition; + const program = { + start: now() + delay, + b + }; + if (!b) { + // @ts-ignore todo: improve typings + program.group = outros; + outros.r += 1; + } + if (running_program || pending_program) { + pending_program = program; + } + else { + // if this is an intro, and there's a delay, we need to do + // an initial tick and/or apply CSS animation immediately + if (css) { + clear_animation(); + animation_name = create_rule(node, t, b, duration, delay, easing, css); + } + if (b) + tick(0, 1); + running_program = init(program, duration); + add_render_callback(() => dispatch(node, b, 'start')); + loop(now => { + if (pending_program && now > pending_program.start) { + running_program = init(pending_program, duration); + pending_program = null; + dispatch(node, running_program.b, 'start'); + if (css) { + clear_animation(); + animation_name = create_rule(node, t, running_program.b, running_program.duration, 0, easing, config.css); + } + } + if (running_program) { + if (now >= running_program.end) { + tick(t = running_program.b, 1 - t); + dispatch(node, running_program.b, 'end'); + if (!pending_program) { + // we're done + if (running_program.b) { + // intro — we can tidy up immediately + clear_animation(); + } + else { + // outro — needs to be coordinated + if (!--running_program.group.r) + run_all(running_program.group.c); + } + } + running_program = null; + } + else if (now >= running_program.start) { + const p = now - running_program.start; + t = running_program.a + running_program.d * easing(p / running_program.duration); + tick(t, 1 - t); + } + } + return !!(running_program || pending_program); + }); + } + } + return { + run(b) { + if (is_function(config)) { + wait().then(() => { + // @ts-ignore + config = config(); + go(b); + }); + } + else { + go(b); + } + }, + end() { + clear_animation(); + running_program = pending_program = null; + } + }; + } + + const globals = (typeof window !== 'undefined' + ? window + : typeof globalThis !== 'undefined' + ? globalThis + : global); + function create_component(block) { + block && block.c(); + } + function mount_component(component, target, anchor) { + const { fragment, on_mount, on_destroy, after_update } = component.$$; + fragment && fragment.m(target, anchor); + // onMount happens before the initial afterUpdate + add_render_callback(() => { + const new_on_destroy = on_mount.map(run).filter(is_function); + if (on_destroy) { + on_destroy.push(...new_on_destroy); + } + else { + // Edge case - component was destroyed immediately, + // most likely as a result of a binding initialising + run_all(new_on_destroy); + } + component.$$.on_mount = []; + }); + after_update.forEach(add_render_callback); + } + function destroy_component(component, detaching) { + const $$ = component.$$; + if ($$.fragment !== null) { + run_all($$.on_destroy); + $$.fragment && $$.fragment.d(detaching); + // TODO null out other refs, including component.$$ (but need to + // preserve final state?) + $$.on_destroy = $$.fragment = null; + $$.ctx = []; + } + } + function make_dirty(component, i) { + if (component.$$.dirty[0] === -1) { + dirty_components.push(component); + schedule_update(); + component.$$.dirty.fill(0); + } + component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); + } + function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { + const parent_component = current_component; + set_current_component(component); + const $$ = component.$$ = { + fragment: null, + ctx: null, + // state + props, + update: noop, + not_equal, + bound: blank_object(), + // lifecycle + on_mount: [], + on_destroy: [], + before_update: [], + after_update: [], + context: new Map(parent_component ? parent_component.$$.context : []), + // everything else + callbacks: blank_object(), + dirty, + skip_bound: false + }; + let ready = false; + $$.ctx = instance + ? instance(component, options.props || {}, (i, ret, ...rest) => { + const value = rest.length ? rest[0] : ret; + if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { + if (!$$.skip_bound && $$.bound[i]) + $$.bound[i](value); + if (ready) + make_dirty(component, i); + } + return ret; + }) + : []; + $$.update(); + ready = true; + run_all($$.before_update); + // `false` as a special case of no DOM component + $$.fragment = create_fragment ? create_fragment($$.ctx) : false; + if (options.target) { + if (options.hydrate) { + const nodes = children(options.target); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + $$.fragment && $$.fragment.l(nodes); + nodes.forEach(detach); + } + else { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + $$.fragment && $$.fragment.c(); + } + if (options.intro) + transition_in(component.$$.fragment); + mount_component(component, options.target, options.anchor); + flush(); + } + set_current_component(parent_component); + } + /** + * Base class for Svelte components. Used when dev=false. + */ + class SvelteComponent { + $destroy() { + destroy_component(this, 1); + this.$destroy = noop; + } + $on(type, callback) { + const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); + callbacks.push(callback); + return () => { + const index = callbacks.indexOf(callback); + if (index !== -1) + callbacks.splice(index, 1); + }; + } + $set($$props) { + if (this.$$set && !is_empty($$props)) { + this.$$.skip_bound = true; + this.$$set($$props); + this.$$.skip_bound = false; + } + } + } + + const subscriber_queue = []; + /** + * Create a `Writable` store that allows both updating and reading by subscription. + * @param {*=}value initial value + * @param {StartStopNotifier=}start start and stop notifications for subscriptions + */ + function writable(value, start = noop) { + let stop; + const subscribers = []; + function set(new_value) { + if (safe_not_equal(value, new_value)) { + value = new_value; + if (stop) { // store is ready + const run_queue = !subscriber_queue.length; + for (let i = 0; i < subscribers.length; i += 1) { + const s = subscribers[i]; + s[1](); + subscriber_queue.push(s, value); + } + if (run_queue) { + for (let i = 0; i < subscriber_queue.length; i += 2) { + subscriber_queue[i][0](subscriber_queue[i + 1]); + } + subscriber_queue.length = 0; + } + } + } + } + function update(fn) { + set(fn(value)); + } + function subscribe(run, invalidate = noop) { + const subscriber = [run, invalidate]; + subscribers.push(subscriber); + if (subscribers.length === 1) { + stop = start(set) || noop; + } + run(value); + return () => { + const index = subscribers.indexOf(subscriber); + if (index !== -1) { + subscribers.splice(index, 1); + } + if (subscribers.length === 0) { + stop(); + stop = null; + } + }; + } + return { set, update, subscribe }; + } + + function log(message) { + // eslint-disable-next-line + console.log( + '%c wails bridge %c ' + message + ' ', + 'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem', + 'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem' + ); + } + + /** Overlay */ + const overlayVisible = writable(false); + + function showOverlay() { + overlayVisible.set(true); + } + function hideOverlay() { + overlayVisible.set(false); + } + + /** Menubar **/ + const menuVisible = writable(false); + + /** Trays **/ + + const trays = writable([]); + function setTray(tray) { + trays.update((current) => { + // Remove existing if it exists, else add + const index = current.findIndex(item => item.ID === tray.ID); + if ( index === -1 ) { + current.push(tray); + } else { + current[index] = tray; + } + return current; + }); + } + function updateTrayLabel(tray) { + trays.update((current) => { + // Remove existing if it exists, else add + const index = current.findIndex(item => item.ID === tray.ID); + if ( index === -1 ) { + return log("ERROR: Attempted to update tray index ", tray.ID) + } + current[index].Label = tray.Label; + return current; + }); + } + + function deleteTrayMenu(id) { + trays.update((current) => { + // Remove existing if it exists, else add + const index = current.findIndex(item => item.ID === id); + if ( index === -1 ) { + return log("ERROR: Attempted to delete tray index ") + } + current.splice(index, 1); + return current; + }); + } + + let selectedMenu = writable(null); + + function fade(node, { delay = 0, duration = 400, easing = identity } = {}) { + const o = +getComputedStyle(node).opacity; + return { + delay, + duration, + easing, + css: t => `opacity: ${t * o}` + }; + } + + /* Overlay.svelte generated by Svelte v3.32.2 */ + + function add_css() { + var style = element("style"); + style.id = "svelte-1m56lfo-style"; + style.textContent = ".wails-reconnect-overlay.svelte-1m56lfo{position:fixed;top:0;left:0;width:100%;height:100%;backdrop-filter:blur(20px) saturate(160%) contrast(45%) brightness(140%);z-index:999999\n }.wails-reconnect-overlay-content.svelte-1m56lfo{position:relative;top:50%;transform:translateY(-50%);margin:0;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAAA7CAMAAAAEsocZAAAC91BMVEUAAACzQ0PjMjLkMjLZLS7XLS+vJCjkMjKlEx6uGyHjMDGiFx7GJyrAISjUKy3mMzPlMjLjMzOsGyDKJirkMjK6HyXmMjLgMDC6IiLcMjLULC3MJyrRKSy+IibmMzPmMjK7ISXlMjLIJimzHSLkMjKtGiHZLC7BIifgMDCpGSDFIivcLy+yHSKoGR+eFBzNKCvlMjKxHSPkMTKxHSLmMjLKJyq5ICXDJCe6ISXdLzDkMjLmMzPFJSm2HyTlMTLhMDGyHSKUEBmhFx24HyTCJCjHJijjMzOiFh7mMjJ6BhDaLDCuGyOKABjnMzPGJinJJiquHCGEChSmGB/pMzOiFh7VKy3OKCu1HiSvHCLjMTLMKCrBIyeICxWxHCLDIyjSKizBIyh+CBO9ISa6ISWDChS9Iie1HyXVLC7FJSrLKCrlMjLiMTGPDhicFRywGyKXFBuhFx1/BxO7IiXkMTGeFBx8BxLkMTGnGR/GJCi4ICWsGyGJDxXSLS2yGiHSKi3CJCfnMzPQKiyECRTKJiq6ISWUERq/Iye0HiPDJCjGJSm6ICaPDxiTEBrdLy+3HyXSKiy0HyOQEBi4ICWhFh1+CBO9IieODhfSKyzWLC2LDhh8BxHKKCq7ISWaFBzkMzPqNDTTLC3EJSiHDBacExyvGyO1HyTPKCy+IieoGSC7ISaVEhrMKCvQKyusGyG0HiKACBPIJSq/JCaABxR5BRLEJCnkMzPJJinEJimPDRZ2BRKqHx/jMjLnMzPgMDHULC3NKSvQKSzsNDTWLS7SKyy3HyTKJyrDJSjbLzDYLC6mGB/GJSnVLC61HiPLKCrHJSm/Iye8Iia6ICWzHSKxHCLaLi/PKSupGR+7ICXpMzPbLi/IJinJJSmsGyGrGiCkFx6PDheJCxaFChXBIyfAIieSDxmBCBPlMjLeLzDdLzC5HySMDRe+ISWvGyGcFBzSKSzPJyvMJyrEJCjDIyefFRyWERriMDHUKiy/ISaZExv0NjbwNTXuNDTrMzMI0c+yAAAAu3RSTlMAA8HR/gwGgAj+MEpGCsC+hGpjQjYnIxgWBfzx7urizMrFqqB1bF83KhsR/fz8+/r5+fXv7unZ1tC+t6mmopqKdW1nYVpVRjUeHhIQBPr59/b28/Hx8ODg3NvUw8O/vKeim5aNioiDgn1vZWNjX1xUU1JPTUVFPT08Mi4qJyIh/Pv7+/n4+Pf39fT08/Du7efn5uXj4uHa19XNwsG/vrq2tbSuramlnpyYkpGNiIZ+enRraGVjVVBKOzghdjzRsAAABJVJREFUWMPtllVQG1EYhTc0ASpoobS0FCulUHd3oUjd3d3d3d3d3d2b7CYhnkBCCHGDEIK7Vh56d0NpOgwkYfLQzvA9ZrLfnPvfc+8uVEst/yheBJup3Nya2MjU6pa/jWLZtxjXpZFtVB4uVNI6m5gIruNkVFebqIb5Ug2ym4TIEM/gtUOGbg613oBzjAzZFrZ+lXu/3TIiMXXS5M6HTvrNHeLpZLEh6suGNW9fzZ9zd/qVi2eOHygqi5cDE5GUrJocONgzyqo0UXNSUlKSEhMztFqtXq9vNxImAmS3g7Y6QlbjdBWVGW36jt4wDGTUXjUsafh5zJWRkdFuZGtWGnCRmg+HasiGMUClTTzW0ZuVgLlGDIPM4Lhi0IrVq+tv2hS21fNrSONQgpM9DsJ4t3fM9PkvJuKj2ZjrZwvILKvaSTgciUSirjt6dOfOpyd169bDb9rMOwF9Hj4OD100gY0YXYb299bjzMrqj9doNByJWlVXFB9DT5dmJuvy+cq83JyuS6ayEYSHulKL8dmFnBkrCeZlHKMrC5XRhXGCZB2Ty1fkleRQaMCFT2DBsEafzRFJu7/2MicbKynPhQUDLiZwMWLJZKNLzoLbJBYVcurSmbmn+rcyJ8vCMgmlmaW6gnwun/+3C96VpAUuET1ZgRR36r2xWlnYSnf3oKABA14uXDDvydxHs6cpTV1p3hlJ2rJCiUjIZCByItXg8sHJijuvT64CuMTABUYvb6NN1Jdp1PH7D7f3bo2eS5KvW4RJr7atWT5w4MBBg9zdBw9+37BS7QIoFS5WnIaj12dr1DEXFgdvr4fh4eFl+u/wz8uf3jjHic8s4DL2Dal0IANyUBeCRCcwOBJV26JsjSpGwHVuSai69jvqD+jr56OgtKy0zAAK5mLTVBKVKL5tNthGAR9JneJQ/bFsHNzy+U7IlCYROxtMpIjR0ceoQVnowracLLpAQWETqV361bPoFo3cEbz2zYLZM7t3HWXcxmiBOgttS1ycWkTXMWh4mGigdug9DFdttqCFgTN6nD0q1XEVSoCxEjyFCi2eNC6Z69MRVIImJ6JQSf5gcFVCuF+aDhCa1F6MJFDaiNBQAh2TMfWBjhmLsAxUjG/fmjs0qjJck8D0GPBcuUuZW1LS/tIsPzqmQt17PvZQknlwnf4tHDBc+7t5VV3QQCkdc+Ur8/hdrz0but0RCumWiYbiKmLJ7EVbRomj4Q7+y5wsaXvfTGFpQcHB7n2WbG4MGdniw2Tm8xl5Yhr7MrSYHQ3uampz10aWyHyuzxvqaW/6W4MjXAUD3QV2aw97ZxhGjxCohYf5TpTHMXU1BbsAuoFnkRygVieIGAbqiF7rrH4rfWpKJouBCtyHJF8ctEyGubBa+C6NsMYEUonJFITHZqWBxXUA12Dv76Tf/PgOBmeNiiLG1pcKo1HAq8jLpY4JU1yWEixVNaOgoRJAKBSZHTZTU+wJOMtUDZvlVITC6FTlksyrEBoPHXpxxbzdaqzigUtVDkJVIOtVQ9UEOR4VGUh/kHWq0edJ6CxnZ+eePXva2bnY/cF/I1RLLf8vvwDANdMSMegxcAAAAABJRU5ErkJggg==);background-repeat:no-repeat;background-position:center\n }.wails-reconnect-overlay-loadingspinner.svelte-1m56lfo{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#f00 #eee0 #f00 #eee0;border-radius:50%;animation:svelte-1m56lfo-loadingspin 1s linear infinite;margin:auto;padding:2.5em\n }@keyframes svelte-1m56lfo-loadingspin{100%{transform:rotate(360deg)}}"; + append(document.head, style); + } + + // (8:0) {#if $overlayVisible } + function create_if_block(ctx) { + let div2; + let div2_transition; + let current; + + return { + c() { + div2 = element("div"); + div2.innerHTML = `
`; + attr(div2, "class", "wails-reconnect-overlay svelte-1m56lfo"); + }, + m(target, anchor) { + insert(target, div2, anchor); + current = true; + }, + i(local) { + if (current) return; + + add_render_callback(() => { + if (!div2_transition) div2_transition = create_bidirectional_transition(div2, fade, { duration: 200 }, true); + div2_transition.run(1); + }); + + current = true; + }, + o(local) { + if (!div2_transition) div2_transition = create_bidirectional_transition(div2, fade, { duration: 200 }, false); + div2_transition.run(0); + current = false; + }, + d(detaching) { + if (detaching) detach(div2); + if (detaching && div2_transition) div2_transition.end(); + } + }; + } + + function create_fragment(ctx) { + let if_block_anchor; + let current; + let if_block = /*$overlayVisible*/ ctx[0] && create_if_block(); + + return { + c() { + if (if_block) if_block.c(); + if_block_anchor = empty(); + }, + m(target, anchor) { + if (if_block) if_block.m(target, anchor); + insert(target, if_block_anchor, anchor); + current = true; + }, + p(ctx, [dirty]) { + if (/*$overlayVisible*/ ctx[0]) { + if (if_block) { + if (dirty & /*$overlayVisible*/ 1) { + transition_in(if_block, 1); + } + } else { + if_block = create_if_block(); + if_block.c(); + transition_in(if_block, 1); + if_block.m(if_block_anchor.parentNode, if_block_anchor); + } + } else if (if_block) { + group_outros(); + + transition_out(if_block, 1, 1, () => { + if_block = null; + }); + + check_outros(); + } + }, + i(local) { + if (current) return; + transition_in(if_block); + current = true; + }, + o(local) { + transition_out(if_block); + current = false; + }, + d(detaching) { + if (if_block) if_block.d(detaching); + if (detaching) detach(if_block_anchor); + } + }; + } + + function instance($$self, $$props, $$invalidate) { + let $overlayVisible; + component_subscribe($$self, overlayVisible, $$value => $$invalidate(0, $overlayVisible = $$value)); + return [$overlayVisible]; + } + + class Overlay extends SvelteComponent { + constructor(options) { + super(); + if (!document.getElementById("svelte-1m56lfo-style")) add_css(); + init(this, options, instance, create_fragment, safe_not_equal, {}); + } + } + + /* Menu.svelte generated by Svelte v3.32.2 */ + + function add_css$1() { + var style = element("style"); + style.id = "svelte-1oysp7o-style"; + style.textContent = ".menu.svelte-1oysp7o.svelte-1oysp7o{padding:3px;background-color:#0008;color:#EEF;border-radius:5px;margin-top:5px;position:absolute;backdrop-filter:blur(3px) saturate(160%) contrast(45%) brightness(140%);border:1px solid rgb(88,88,88);box-shadow:0 0 1px rgb(146,146,148) inset}.menuitem.svelte-1oysp7o.svelte-1oysp7o{display:flex;align-items:center;padding:1px 5px}.menuitem.svelte-1oysp7o.svelte-1oysp7o:hover{display:flex;align-items:center;background-color:rgb(57,131,223);padding:1px 5px;border-radius:5px}.menuitem.svelte-1oysp7o img.svelte-1oysp7o{padding-right:5px}"; + append(document.head, style); + } + + function get_each_context(ctx, list, i) { + const child_ctx = ctx.slice(); + child_ctx[2] = list[i]; + return child_ctx; + } + + // (8:0) {#if !hidden} + function create_if_block$1(ctx) { + let div; + let if_block = /*menu*/ ctx[0].Menu && create_if_block_1(ctx); + + return { + c() { + div = element("div"); + if (if_block) if_block.c(); + attr(div, "class", "menu svelte-1oysp7o"); + }, + m(target, anchor) { + insert(target, div, anchor); + if (if_block) if_block.m(div, null); + }, + p(ctx, dirty) { + if (/*menu*/ ctx[0].Menu) { + if (if_block) { + if_block.p(ctx, dirty); + } else { + if_block = create_if_block_1(ctx); + if_block.c(); + if_block.m(div, null); + } + } else if (if_block) { + if_block.d(1); + if_block = null; + } + }, + d(detaching) { + if (detaching) detach(div); + if (if_block) if_block.d(); + } + }; + } + + // (10:4) {#if menu.Menu } + function create_if_block_1(ctx) { + let each_1_anchor; + let each_value = /*menu*/ ctx[0].Menu.Items; + let each_blocks = []; + + for (let i = 0; i < each_value.length; i += 1) { + each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i)); + } + + return { + c() { + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].c(); + } + + each_1_anchor = empty(); + }, + m(target, anchor) { + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].m(target, anchor); + } + + insert(target, each_1_anchor, anchor); + }, + p(ctx, dirty) { + if (dirty & /*menu*/ 1) { + each_value = /*menu*/ ctx[0].Menu.Items; + let i; + + for (i = 0; i < each_value.length; i += 1) { + const child_ctx = get_each_context(ctx, each_value, i); + + if (each_blocks[i]) { + each_blocks[i].p(child_ctx, dirty); + } else { + each_blocks[i] = create_each_block(child_ctx); + each_blocks[i].c(); + each_blocks[i].m(each_1_anchor.parentNode, each_1_anchor); + } + } + + for (; i < each_blocks.length; i += 1) { + each_blocks[i].d(1); + } + + each_blocks.length = each_value.length; + } + }, + d(detaching) { + destroy_each(each_blocks, detaching); + if (detaching) detach(each_1_anchor); + } + }; + } + + // (13:12) {#if menuItem.Image } + function create_if_block_2(ctx) { + let div; + let img; + let img_src_value; + + return { + c() { + div = element("div"); + img = element("img"); + attr(img, "alt", ""); + if (img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[2].Image)) attr(img, "src", img_src_value); + attr(img, "class", "svelte-1oysp7o"); + }, + m(target, anchor) { + insert(target, div, anchor); + append(div, img); + }, + p(ctx, dirty) { + if (dirty & /*menu*/ 1 && img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[2].Image)) { + attr(img, "src", img_src_value); + } + }, + d(detaching) { + if (detaching) detach(div); + } + }; + } + + // (11:8) {#each menu.Menu.Items as menuItem} + function create_each_block(ctx) { + let div1; + let t0; + let div0; + let t1_value = /*menuItem*/ ctx[2].Label + ""; + let t1; + let t2; + let if_block = /*menuItem*/ ctx[2].Image && create_if_block_2(ctx); + + return { + c() { + div1 = element("div"); + if (if_block) if_block.c(); + t0 = space(); + div0 = element("div"); + t1 = text(t1_value); + t2 = space(); + attr(div0, "class", "menulabel"); + attr(div1, "class", "menuitem svelte-1oysp7o"); + }, + m(target, anchor) { + insert(target, div1, anchor); + if (if_block) if_block.m(div1, null); + append(div1, t0); + append(div1, div0); + append(div0, t1); + append(div1, t2); + }, + p(ctx, dirty) { + if (/*menuItem*/ ctx[2].Image) { + if (if_block) { + if_block.p(ctx, dirty); + } else { + if_block = create_if_block_2(ctx); + if_block.c(); + if_block.m(div1, t0); + } + } else if (if_block) { + if_block.d(1); + if_block = null; + } + + if (dirty & /*menu*/ 1 && t1_value !== (t1_value = /*menuItem*/ ctx[2].Label + "")) set_data(t1, t1_value); + }, + d(detaching) { + if (detaching) detach(div1); + if (if_block) if_block.d(); + } + }; + } + + function create_fragment$1(ctx) { + let if_block_anchor; + let if_block = !/*hidden*/ ctx[1] && create_if_block$1(ctx); + + return { + c() { + if (if_block) if_block.c(); + if_block_anchor = empty(); + }, + m(target, anchor) { + if (if_block) if_block.m(target, anchor); + insert(target, if_block_anchor, anchor); + }, + p(ctx, [dirty]) { + if (!/*hidden*/ ctx[1]) { + if (if_block) { + if_block.p(ctx, dirty); + } else { + if_block = create_if_block$1(ctx); + if_block.c(); + if_block.m(if_block_anchor.parentNode, if_block_anchor); + } + } else if (if_block) { + if_block.d(1); + if_block = null; + } + }, + i: noop, + o: noop, + d(detaching) { + if (if_block) if_block.d(detaching); + if (detaching) detach(if_block_anchor); + } + }; + } + + function instance$1($$self, $$props, $$invalidate) { + let { menu } = $$props; + let { hidden = true } = $$props; + + $$self.$$set = $$props => { + if ("menu" in $$props) $$invalidate(0, menu = $$props.menu); + if ("hidden" in $$props) $$invalidate(1, hidden = $$props.hidden); + }; + + return [menu, hidden]; + } + + class Menu extends SvelteComponent { + constructor(options) { + super(); + if (!document.getElementById("svelte-1oysp7o-style")) add_css$1(); + init(this, options, instance$1, create_fragment$1, safe_not_equal, { menu: 0, hidden: 1 }); + } + } + + /* TrayMenu.svelte generated by Svelte v3.32.2 */ + + const { document: document_1 } = globals; + + function add_css$2() { + var style = element("style"); + style.id = "svelte-esze1k-style"; + style.textContent = ".tray-menu.svelte-esze1k{padding-left:0.5rem;padding-right:0.5rem;overflow:visible;font-size:14px}.label.svelte-esze1k{text-align:right;padding-right:10px}"; + append(document_1.head, style); + } + + // (47:4) {#if tray.ProcessedMenu } + function create_if_block$2(ctx) { + let menu; + let current; + + menu = new Menu({ + props: { + menu: /*tray*/ ctx[0].ProcessedMenu, + hidden: /*hidden*/ ctx[1] + } + }); + + return { + c() { + create_component(menu.$$.fragment); + }, + m(target, anchor) { + mount_component(menu, target, anchor); + current = true; + }, + p(ctx, dirty) { + const menu_changes = {}; + if (dirty & /*tray*/ 1) menu_changes.menu = /*tray*/ ctx[0].ProcessedMenu; + if (dirty & /*hidden*/ 2) menu_changes.hidden = /*hidden*/ ctx[1]; + menu.$set(menu_changes); + }, + i(local) { + if (current) return; + transition_in(menu.$$.fragment, local); + current = true; + }, + o(local) { + transition_out(menu.$$.fragment, local); + current = false; + }, + d(detaching) { + destroy_component(menu, detaching); + } + }; + } + + function create_fragment$2(ctx) { + let span1; + let span0; + let t0_value = /*tray*/ ctx[0].Label + ""; + let t0; + let t1; + let current; + let mounted; + let dispose; + let if_block = /*tray*/ ctx[0].ProcessedMenu && create_if_block$2(ctx); + + return { + c() { + span1 = element("span"); + span0 = element("span"); + t0 = text(t0_value); + t1 = space(); + if (if_block) if_block.c(); + attr(span0, "class", "label svelte-esze1k"); + attr(span1, "class", "tray-menu svelte-esze1k"); + }, + m(target, anchor) { + insert(target, span1, anchor); + append(span1, span0); + append(span0, t0); + append(span1, t1); + if (if_block) if_block.m(span1, null); + current = true; + + if (!mounted) { + dispose = [ + listen(span0, "click", /*trayClicked*/ ctx[3]), + action_destroyer(clickOutside.call(null, span1)), + listen(span1, "click_outside", /*closeMenu*/ ctx[2]) + ]; + + mounted = true; + } + }, + p(ctx, [dirty]) { + if ((!current || dirty & /*tray*/ 1) && t0_value !== (t0_value = /*tray*/ ctx[0].Label + "")) set_data(t0, t0_value); + + if (/*tray*/ ctx[0].ProcessedMenu) { + if (if_block) { + if_block.p(ctx, dirty); + + if (dirty & /*tray*/ 1) { + transition_in(if_block, 1); + } + } else { + if_block = create_if_block$2(ctx); + if_block.c(); + transition_in(if_block, 1); + if_block.m(span1, null); + } + } else if (if_block) { + group_outros(); + + transition_out(if_block, 1, 1, () => { + if_block = null; + }); + + check_outros(); + } + }, + i(local) { + if (current) return; + transition_in(if_block); + current = true; + }, + o(local) { + transition_out(if_block); + current = false; + }, + d(detaching) { + if (detaching) detach(span1); + if (if_block) if_block.d(); + mounted = false; + run_all(dispose); + } + }; + } + + function clickOutside(node) { + const handleClick = event => { + if (node && !node.contains(event.target) && !event.defaultPrevented) { + node.dispatchEvent(new CustomEvent("click_outside", node)); + } + }; + + document.addEventListener("click", handleClick, true); + + return { + destroy() { + document.removeEventListener("click", handleClick, true); + } + }; + } + + function instance$2($$self, $$props, $$invalidate) { + let hidden; + let $selectedMenu; + component_subscribe($$self, selectedMenu, $$value => $$invalidate(4, $selectedMenu = $$value)); + let { tray = null } = $$props; + + function closeMenu() { + selectedMenu.set(null); + } + + function trayClicked() { + if ($selectedMenu !== tray) { + selectedMenu.set(tray); + } else { + selectedMenu.set(null); + } + } + + $$self.$$set = $$props => { + if ("tray" in $$props) $$invalidate(0, tray = $$props.tray); + }; + + $$self.$$.update = () => { + if ($$self.$$.dirty & /*$selectedMenu, tray*/ 17) { + $$invalidate(1, hidden = $selectedMenu !== tray); + } + }; + + return [tray, hidden, closeMenu, trayClicked, $selectedMenu]; + } + + class TrayMenu extends SvelteComponent { + constructor(options) { + super(); + if (!document_1.getElementById("svelte-esze1k-style")) add_css$2(); + init(this, options, instance$2, create_fragment$2, safe_not_equal, { tray: 0 }); + } + } + + /* Menubar.svelte generated by Svelte v3.32.2 */ + + function add_css$3() { + var style = element("style"); + style.id = "svelte-1i0zb4n-style"; + style.textContent = ".tray-menus.svelte-1i0zb4n{display:flex;flex-direction:row;justify-content:flex-end}.wails-menubar.svelte-1i0zb4n{position:relative;display:block;top:0;height:2rem;width:100%;border-bottom:1px solid #b3b3b3;box-shadow:0 0 10px 0 #33333360}.time.svelte-1i0zb4n{padding-left:0.5rem;padding-right:1.5rem;overflow:visible;font-size:14px}"; + append(document.head, style); + } + + function get_each_context$1(ctx, list, i) { + const child_ctx = ctx.slice(); + child_ctx[9] = list[i]; + return child_ctx; + } + + // (38:0) {#if $menuVisible } + function create_if_block$3(ctx) { + let div; + let span1; + let t0; + let span0; + let t1; + let div_transition; + let current; + let each_value = /*$trays*/ ctx[2]; + let each_blocks = []; + + for (let i = 0; i < each_value.length; i += 1) { + each_blocks[i] = create_each_block$1(get_each_context$1(ctx, each_value, i)); + } + + const out = i => transition_out(each_blocks[i], 1, 1, () => { + each_blocks[i] = null; + }); + + return { + c() { + div = element("div"); + span1 = element("span"); + + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].c(); + } + + t0 = space(); + span0 = element("span"); + t1 = text(/*dateTimeString*/ ctx[0]); + attr(span0, "class", "time svelte-1i0zb4n"); + attr(span1, "class", "tray-menus svelte-1i0zb4n"); + attr(div, "class", "wails-menubar svelte-1i0zb4n"); + }, + m(target, anchor) { + insert(target, div, anchor); + append(div, span1); + + for (let i = 0; i < each_blocks.length; i += 1) { + each_blocks[i].m(span1, null); + } + + append(span1, t0); + append(span1, span0); + append(span0, t1); + current = true; + }, + p(ctx, dirty) { + if (dirty & /*$trays*/ 4) { + each_value = /*$trays*/ ctx[2]; + let i; + + for (i = 0; i < each_value.length; i += 1) { + const child_ctx = get_each_context$1(ctx, each_value, i); + + if (each_blocks[i]) { + each_blocks[i].p(child_ctx, dirty); + transition_in(each_blocks[i], 1); + } else { + each_blocks[i] = create_each_block$1(child_ctx); + each_blocks[i].c(); + transition_in(each_blocks[i], 1); + each_blocks[i].m(span1, t0); + } + } + + group_outros(); + + for (i = each_value.length; i < each_blocks.length; i += 1) { + out(i); + } + + check_outros(); + } + + if (!current || dirty & /*dateTimeString*/ 1) set_data(t1, /*dateTimeString*/ ctx[0]); + }, + i(local) { + if (current) return; + + for (let i = 0; i < each_value.length; i += 1) { + transition_in(each_blocks[i]); + } + + add_render_callback(() => { + if (!div_transition) div_transition = create_bidirectional_transition(div, fade, {}, true); + div_transition.run(1); + }); + + current = true; + }, + o(local) { + each_blocks = each_blocks.filter(Boolean); + + for (let i = 0; i < each_blocks.length; i += 1) { + transition_out(each_blocks[i]); + } + + if (!div_transition) div_transition = create_bidirectional_transition(div, fade, {}, false); + div_transition.run(0); + current = false; + }, + d(detaching) { + if (detaching) detach(div); + destroy_each(each_blocks, detaching); + if (detaching && div_transition) div_transition.end(); + } + }; + } + + // (41:4) {#each $trays as tray} + function create_each_block$1(ctx) { + let traymenu; + let current; + traymenu = new TrayMenu({ props: { tray: /*tray*/ ctx[9] } }); + + return { + c() { + create_component(traymenu.$$.fragment); + }, + m(target, anchor) { + mount_component(traymenu, target, anchor); + current = true; + }, + p(ctx, dirty) { + const traymenu_changes = {}; + if (dirty & /*$trays*/ 4) traymenu_changes.tray = /*tray*/ ctx[9]; + traymenu.$set(traymenu_changes); + }, + i(local) { + if (current) return; + transition_in(traymenu.$$.fragment, local); + current = true; + }, + o(local) { + transition_out(traymenu.$$.fragment, local); + current = false; + }, + d(detaching) { + destroy_component(traymenu, detaching); + } + }; + } + + function create_fragment$3(ctx) { + let if_block_anchor; + let current; + let mounted; + let dispose; + let if_block = /*$menuVisible*/ ctx[1] && create_if_block$3(ctx); + + return { + c() { + if (if_block) if_block.c(); + if_block_anchor = empty(); + }, + m(target, anchor) { + if (if_block) if_block.m(target, anchor); + insert(target, if_block_anchor, anchor); + current = true; + + if (!mounted) { + dispose = listen(window, "keydown", /*handleKeydown*/ ctx[3]); + mounted = true; + } + }, + p(ctx, [dirty]) { + if (/*$menuVisible*/ ctx[1]) { + if (if_block) { + if_block.p(ctx, dirty); + + if (dirty & /*$menuVisible*/ 2) { + transition_in(if_block, 1); + } + } else { + if_block = create_if_block$3(ctx); + if_block.c(); + transition_in(if_block, 1); + if_block.m(if_block_anchor.parentNode, if_block_anchor); + } + } else if (if_block) { + group_outros(); + + transition_out(if_block, 1, 1, () => { + if_block = null; + }); + + check_outros(); + } + }, + i(local) { + if (current) return; + transition_in(if_block); + current = true; + }, + o(local) { + transition_out(if_block); + current = false; + }, + d(detaching) { + if (if_block) if_block.d(detaching); + if (detaching) detach(if_block_anchor); + mounted = false; + dispose(); + } + }; + } + + function instance$3($$self, $$props, $$invalidate) { + let day; + let dom; + let mon; + let currentTime; + let dateTimeString; + let $menuVisible; + let $trays; + component_subscribe($$self, menuVisible, $$value => $$invalidate(1, $menuVisible = $$value)); + component_subscribe($$self, trays, $$value => $$invalidate(2, $trays = $$value)); + let time = new Date(); + + onMount(() => { + const interval = setInterval( + () => { + $$invalidate(4, time = new Date()); + }, + 1000 + ); + + return () => { + clearInterval(interval); + }; + }); + + function handleKeydown(e) { + // Backtick toggle + if (e.keyCode == 192) { + menuVisible.update(current => { + return !current; + }); + } + } + + $$self.$$.update = () => { + if ($$self.$$.dirty & /*time*/ 16) { + $$invalidate(5, day = time.toLocaleString("default", { weekday: "short" })); + } + + if ($$self.$$.dirty & /*time*/ 16) { + $$invalidate(6, dom = time.getDate()); + } + + if ($$self.$$.dirty & /*time*/ 16) { + $$invalidate(7, mon = time.toLocaleString("default", { month: "short" })); + } + + if ($$self.$$.dirty & /*time*/ 16) { + $$invalidate(8, currentTime = time.toLocaleString("en-US", { + hour: "numeric", + minute: "numeric", + hour12: true + }).toLowerCase()); + } + + if ($$self.$$.dirty & /*day, dom, mon, currentTime*/ 480) { + $$invalidate(0, dateTimeString = `${day} ${dom} ${mon} ${currentTime}`); + } + }; + + return [ + dateTimeString, + $menuVisible, + $trays, + handleKeydown, + time, + day, + dom, + mon, + currentTime + ]; + } + + class Menubar extends SvelteComponent { + constructor(options) { + super(); + if (!document.getElementById("svelte-1i0zb4n-style")) add_css$3(); + init(this, options, instance$3, create_fragment$3, safe_not_equal, {}); + } + } + + /* + _ __ _ __ + | | / /___ _(_) /____ + | | /| / / __ `/ / / ___/ + | |/ |/ / /_/ / / (__ ) + |__/|__/\__,_/_/_/____/ + The electron alternative for Go + (c) Lea Anthony 2019-present + */ + + let websocket = null; + let callback = null; + let connectTimer; + + function StartWebsocket(userCallback) { + + callback = userCallback; + + window.onbeforeunload = function() { + if( websocket ) { + websocket.onclose = function () { }; + websocket.close(); + websocket = null; + } + }; + + // ...and attempt to connect + connect(); + + } + + function setupIPCBridge() { + window.wailsInvoke = (message) => { + websocket.send(message); + }; + } + + // Handles incoming websocket connections + function handleConnect() { + log('Connected to backend'); + setupIPCBridge(); + hideOverlay(); + clearInterval(connectTimer); + websocket.onclose = handleDisconnect; + websocket.onmessage = handleMessage; + } + + // Handles websocket disconnects + function handleDisconnect() { + log('Disconnected from backend'); + websocket = null; + showOverlay(); + connect(); + } + + // Try to connect to the backend every 1s (default value). + function connect() { + connectTimer = setInterval(function () { + if (websocket == null) { + websocket = new WebSocket('ws://' + window.location.hostname + ':34115/bridge'); + websocket.onopen = handleConnect; + websocket.onerror = function (e) { + e.stopImmediatePropagation(); + e.stopPropagation(); + e.preventDefault(); + websocket = null; + return false; + }; + } + }, 1000); + } + + // Adds a script to the Dom. + // Removes it if second parameter is true. + function addScript(script, remove) { + const s = document.createElement('script'); + s.setAttribute('type', 'text/javascript'); + s.textContent = script; + document.head.appendChild(s); + + // Remove internal messages from the DOM + if (remove) { + s.parentNode.removeChild(s); + } + } + + function handleMessage(message) { + // As a bridge we ignore js and css injections + switch (message.data[0]) { + // Wails library - inject! + case 'b': + message = message.data.slice(1); + addScript(message); + log('Loaded Wails Runtime'); + + // We need to now send a message to the backend telling it + // we have loaded (System Start) + window.wailsInvoke('SS'); + + // Now wails runtime is loaded, wails for the ready event + // and callback to the main app + // window.wails.Events.On('wails:loaded', function () { + if (callback) { + log('Notifying application'); + callback(window.wails); + } + // }); + break; + // Notifications + case 'n': + window.wails._.Notify(message.data.slice(1)); + break; + // // Binding + // case 'b': + // const binding = message.data.slice(1); + // //log("Binding: " + binding) + // window.wails._.NewBinding(binding); + // break; + // // Call back + case 'c': + const callbackData = message.data.slice(1); + window.wails._.Callback(callbackData); + break; + // Tray + case 'T': + const trayMessage = message.data.slice(1); + switch (trayMessage[0]) { + case 'S': + // Set tray + const trayJSON = trayMessage.slice(1); + let tray = JSON.parse(trayJSON); + setTray(tray); + break; + case 'U': + // Update label + const updateTrayLabelJSON = trayMessage.slice(1); + let trayLabelData = JSON.parse(updateTrayLabelJSON); + updateTrayLabel(trayLabelData); + break; + case 'D': + // Delete Tray Menu + const id = trayMessage.slice(1); + deleteTrayMenu(id); + break; + default: + log('Unknown tray message: ' + message.data); + } + break; + + default: + log('Unknown message: ' + message.data); + } + } + + /* + _ __ _ __ + | | / /___ _(_) /____ + | | /| / / __ `/ / / ___/ + | |/ |/ / /_/ / / (__ ) + |__/|__/\__,_/_/_/____/ + The electron alternative for Go + (c) Lea Anthony 2019-present + */ + + function setupMenuBar() { + new Menubar({ + target: document.body, + }); + } + + // Sets up the overlay + function setupOverlay() { + new Overlay({ + target: document.body, + anchor: document.querySelector('#wails-bridge'), + }); + } + + function InitBridge(callback) { + + setupMenuBar(); + + // Setup the overlay + setupOverlay(); + + // Start by showing the overlay... + showOverlay(); + + // ...and attempt to connect + StartWebsocket(callback); + } + + exports.InitBridge = InitBridge; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); diff --git a/v2/internal/runtime/js/runtime/events.js b/v2/internal/runtime/js/runtime/events.js new file mode 100644 index 000000000..4f1f10600 --- /dev/null +++ b/v2/internal/runtime/js/runtime/events.js @@ -0,0 +1,76 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + + +/** + * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + * @param {number} maxCallbacks + */ +function OnMultiple(eventName, callback, maxCallbacks) { + window.wails.Events.OnMultiple(eventName, callback, maxCallbacks); +} + +/** + * Registers an event listener that will be invoked every time the event is emitted + * + * @export + * @param {string} eventName + * @param {function} callback + */ +function On(eventName, callback) { + OnMultiple(eventName, callback); +} + +/** + * Registers an event listener that will be invoked once then destroyed + * + * @export + * @param {string} eventName + * @param {function} callback + */ +function Once(eventName, callback) { + OnMultiple(eventName, callback, 1); +} + + +/** + * Emit an event with the given name and data + * + * @export + * @param {string} eventName + */ +function Emit(eventName) { + var args = [eventName].slice.call(arguments); + return window.wails.Events.Emit.apply(null, args); +} + +/** + * Registers listeners for when the system theme changes + * + * @export + * @param {function} callback + */ +function OnThemeChange(callback) { + On('wails:system:themechange', callback); +} + +module.exports = { + OnMultiple: OnMultiple, + On: On, + Once: Once, + Emit: Emit, + OnThemeChange: OnThemeChange, +}; \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/init.js b/v2/internal/runtime/js/runtime/init.js new file mode 100644 index 000000000..742ee1dc8 --- /dev/null +++ b/v2/internal/runtime/js/runtime/init.js @@ -0,0 +1,33 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +const bridge = require('./bridge'); + +/** + * ready will execute the callback when Wails has loaded + * and initialised. + * + * @param {function} callback + */ +function ready(callback) { + + // If window.wails exists, we are ready + if( window.wails ) { + return callback(); + } + + // If not we need to setup the bridge + bridge.InitBridge(callback); +} + +module.exports = { + ready: ready, +}; \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/log.js b/v2/internal/runtime/js/runtime/log.js new file mode 100644 index 000000000..9133e2a7a --- /dev/null +++ b/v2/internal/runtime/js/runtime/log.js @@ -0,0 +1,113 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + + +/** + * Log the given message with the backend + * + * @export + * @param {string} message + */ +function Print(message) { + window.wails.Log.Print(message); +} + +/** + * Log the given trace message with the backend + * + * @export + * @param {string} message + */ +function Trace(message) { + window.wails.Log.Trace(message); +} + +/** + * Log the given debug message with the backend + * + * @export + * @param {string} message + */ +function Debug(message) { + window.wails.Log.Debug(message); +} + +/** + * Log the given info message with the backend + * + * @export + * @param {string} message + */ +function Info(message) { + window.wails.Log.Info(message); +} + +/** + * Log the given warning message with the backend + * + * @export + * @param {string} message + */ +function Warning(message) { + window.wails.Log.Warning(message); +} + +/** + * Log the given error message with the backend + * + * @export + * @param {string} message + */ +function Error(message) { + window.wails.Log.Error(message); +} + +/** + * Log the given fatal message with the backend + * + * @param {string} message + */ +function Fatal(message) { + window.wails.Log.Fatal(message); +} + + +/** + * Sets the Log level to the given log level + * + * @param {number} loglevel + */ +function SetLogLevel(loglevel) { + window.wails.Log.SetLogLevel(loglevel); +} + +// Log levels +const Level = { + TRACE: 1, + DEBUG: 2, + INFO: 3, + WARNING: 4, + ERROR: 5, +}; + + +module.exports = { + Print: Print, + Trace: Trace, + Debug: Debug, + Info: Info, + Warning: Warning, + Error: Error, + Fatal: Fatal, + SetLogLevel: SetLogLevel, + Level: Level, +}; diff --git a/v2/internal/runtime/js/runtime/main.js b/v2/internal/runtime/js/runtime/main.js new file mode 100644 index 000000000..25380731f --- /dev/null +++ b/v2/internal/runtime/js/runtime/main.js @@ -0,0 +1,20 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +const Log = require('./log'); +const Events = require('./events'); +const Init = require('./init'); + +module.exports = { + Events: Events, + ready: Init.ready, + Log: Log, +}; \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/package-lock.json b/v2/internal/runtime/js/runtime/package-lock.json new file mode 100644 index 000000000..ee7ac70e4 --- /dev/null +++ b/v2/internal/runtime/js/runtime/package-lock.json @@ -0,0 +1,517 @@ +{ + "name": "@wails/runtime", + "version": "1.3.15", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "dts-dom": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/dts-dom/-/dts-dom-3.6.0.tgz", + "integrity": "sha512-on5jxTgt+A6r0Zyyz6ZRHXaAO7J1VPnOd6+AmvI1vH440AlAZZNc5rUHzgPuTjGlrVr1rOWQYNl7ZJK6rDohbw==", + "dev": true + }, + "dts-gen": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/dts-gen/-/dts-gen-0.5.8.tgz", + "integrity": "sha512-kIAV6dlHaF7r5J+tIuOC1BJls2P72YM0cyWQUR88zcJEpX2ccRZe+HmXLfkkvfPwjvSO3FEqUiyC8On/grx5qw==", + "dev": true, + "requires": { + "dts-dom": "^3.6.0", + "parse-git-config": "^1.1.1", + "typescript": "^3.5.1", + "yargs": "^4.8.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "git-config-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz", + "integrity": "sha1-bTP37WPbDQ4RgTFQO6s6ykfVRmQ=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "fs-exists-sync": "^0.1.0", + "homedir-polyfill": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "parse-git-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-1.1.1.tgz", + "integrity": "sha1-06mYQxcTL1c5hxK7pDjhKVkN34w=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "fs-exists-sync": "^0.1.0", + "git-config-path": "^1.0.1", + "ini": "^1.3.4" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "typescript": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "dev": true, + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } + } +} diff --git a/v2/internal/runtime/js/runtime/package.json b/v2/internal/runtime/js/runtime/package.json new file mode 100644 index 000000000..548838a0f --- /dev/null +++ b/v2/internal/runtime/js/runtime/package.json @@ -0,0 +1,28 @@ +{ + "name": "@wails/runtime", + "version": "1.3.20", + "description": "Wails V2 Javascript runtime library", + "main": "main.js", + "types": "runtime.d.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wailsapp/wails.git" + }, + "keywords": [ + "Wails", + "Javascript", + "Go" + ], + "author": "Lea Anthony ", + "license": "MIT", + "bugs": { + "url": "https://github.com/wailsapp/wails/issues" + }, + "homepage": "https://github.com/wailsapp/wails#readme", + "devDependencies": { + "dts-gen": "^0.5.8" + } +} diff --git a/v2/internal/runtime/js/runtime/runtime.d.ts b/v2/internal/runtime/js/runtime/runtime.d.ts new file mode 100644 index 000000000..2b112d763 --- /dev/null +++ b/v2/internal/runtime/js/runtime/runtime.d.ts @@ -0,0 +1,26 @@ +export = wailsapp__runtime; + +interface Level { + TRACE: 1, + DEBUG: 2, + INFO: 3, + WARNING: 4, + ERROR: 5, +} + +declare const wailsapp__runtime: { + EventsEmit(eventName: string, data?: any): void; + EventsOn(eventName: string, callback: (data?: any) => void): void; + EventsOnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void; + EventsOnce(eventName: string, callback: (data?: any) => void): void; + // Init(callback: () => void): void; + LogTrace(message: string): void; + LogDebug(message: string): void; + LogError(message: string): void; + LogFatal(message: string): void; + LogInfo(message: string): void; + LogWarning(message: string): void; + LogLevel: Level; +}; + + diff --git a/v2/internal/runtime/js/runtime/src/Menu.svelte b/v2/internal/runtime/js/runtime/src/Menu.svelte new file mode 100644 index 000000000..bed890d61 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/Menu.svelte @@ -0,0 +1,56 @@ +s + + +{#if !hidden} + +{/if} + + \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/src/Menubar.svelte b/v2/internal/runtime/js/runtime/src/Menubar.svelte new file mode 100644 index 000000000..eeb87b848 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/Menubar.svelte @@ -0,0 +1,58 @@ + + +{#if $menuVisible } +
+ + + + + + +
+{/if} + + + + \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/src/Overlay.svelte b/v2/internal/runtime/js/runtime/src/Overlay.svelte new file mode 100644 index 000000000..0600e36b9 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/Overlay.svelte @@ -0,0 +1,55 @@ + + +{#if $overlayVisible } +
+
+
+
+
+{/if} + + \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/src/TrayMenu.svelte b/v2/internal/runtime/js/runtime/src/TrayMenu.svelte new file mode 100644 index 000000000..f4c9503c7 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/TrayMenu.svelte @@ -0,0 +1,65 @@ + + + + + + + {tray.Label} + {#if tray.ProcessedMenu } + + {/if} + + + \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/src/log.js b/v2/internal/runtime/js/runtime/src/log.js new file mode 100644 index 000000000..256637e8d --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/log.js @@ -0,0 +1,9 @@ + +export function log(message) { + // eslint-disable-next-line + console.log( + '%c wails bridge %c ' + message + ' ', + 'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem', + 'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem' + ); +} \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/src/main.js b/v2/internal/runtime/js/runtime/src/main.js new file mode 100644 index 000000000..bf421990e --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/main.js @@ -0,0 +1,45 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +import Overlay from './Overlay.svelte'; +import MenuBar from './Menubar.svelte'; +import {showOverlay} from "./store"; +import {StartWebsocket} from "./websocket"; + +let components = {}; + +function setupMenuBar() { + components.menubar = new MenuBar({ + target: document.body, + }); +} + +// Sets up the overlay +function setupOverlay() { + components.overlay = new Overlay({ + target: document.body, + anchor: document.querySelector('#wails-bridge'), + }); +} + +export function InitBridge(callback) { + + setupMenuBar() + + // Setup the overlay + setupOverlay(); + + // Start by showing the overlay... + showOverlay(); + + // ...and attempt to connect + StartWebsocket(callback); +} diff --git a/v2/internal/runtime/js/runtime/src/package-lock.json b/v2/internal/runtime/js/runtime/src/package-lock.json new file mode 100644 index 000000000..5397423c9 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/package-lock.json @@ -0,0 +1,525 @@ +{ + "name": "bridge", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz", + "integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@rollup/plugin-commonjs": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", + "integrity": "sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + }, + "dependencies": { + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + } + } + }, + "@rollup/plugin-node-resolve": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.1.1.tgz", + "integrity": "sha512-zlBXR4eRS+2m79TsUZWhsd0slrHUYdRx4JF+aVQm+MI0wsKdlpC2vlDVjmlGvtZY1vsefOT9w3JxvmWSBei+Lg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/node": { + "version": "14.14.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz", + "integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==", + "dev": true + }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "rollup": { + "version": "2.38.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.5.tgz", + "integrity": "sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ==", + "dev": true, + "requires": { + "fsevents": "~2.3.1" + } + }, + "rollup-plugin-svelte": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz", + "integrity": "sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==", + "dev": true, + "requires": { + "require-relative": "^0.8.7", + "rollup-pluginutils": "^2.8.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + }, + "dependencies": { + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "svelte": { + "version": "3.32.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.32.2.tgz", + "integrity": "sha512-Zxh1MQQl/+vnToKbU1Per+PoMN8Jb2MeKJcGxiOsCGR677hXw7jkMfbnNXq33+dxIzV/HfA4xtoSPJrqeB0VUg==", + "dev": true + }, + "terser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz", + "integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/v2/internal/runtime/js/runtime/src/package.json b/v2/internal/runtime/js/runtime/src/package.json new file mode 100644 index 000000000..1f0d6e224 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/package.json @@ -0,0 +1,21 @@ +{ + "name": "bridge", + "version": "1.0.0", + "description": "Wails bridge", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "npx rollup -c", + "watch": "npx rollup -c -w" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@rollup/plugin-commonjs": "^17.1.0", + "@rollup/plugin-node-resolve": "^11.1.1", + "rollup": "^2.38.5", + "rollup-plugin-svelte": "^7.1.0", + "rollup-plugin-terser": "^7.0.2", + "svelte": "^3.32.2" + } +} diff --git a/v2/internal/runtime/js/runtime/src/rollup.config.js b/v2/internal/runtime/js/runtime/src/rollup.config.js new file mode 100644 index 000000000..cbca60af8 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/rollup.config.js @@ -0,0 +1,37 @@ +import resolve from '@rollup/plugin-node-resolve'; +// import commonjs from '@rollup/plugin-commonjs'; +import svelte from 'rollup-plugin-svelte'; +import { terser } from "rollup-plugin-terser"; + +export default [ + // browser-friendly UMD build + { + input: 'main.js', + output: { + name: 'bridge', + file: '../bridge.js', + format: 'umd', + exports: "named" + }, + plugins: [ + svelte({ + // Optionally, preprocess components with svelte.preprocess: + // https://svelte.dev/docs#svelte_preprocess + // preprocess: { + // style: ({content}) => { + // return transformStyles(content); + // } + // }, + + // Emit CSS as "files" for other plugins to process. default is true + emitCss: false, + + }), + resolve({browser: true}), // so Rollup can find `ms` + // commonjs() // so Rollup can convert `ms` to an ES module + // terser(), + ] + }, + + +]; \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/src/store.js b/v2/internal/runtime/js/runtime/src/store.js new file mode 100644 index 000000000..a0b4571b5 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/store.js @@ -0,0 +1,64 @@ + +import { writable } from 'svelte/store'; +import {log} from "./log"; + +/** Overlay */ +export const overlayVisible = writable(false); + +export function showOverlay() { + overlayVisible.set(true); +} +export function hideOverlay() { + overlayVisible.set(false); +} + +/** Menubar **/ +export const menuVisible = writable(false); + +export function showMenuBar() { + menuVisible.set(true); +} +export function hideMenuBar() { + menuVisible.set(false); +} + +/** Trays **/ + +export const trays = writable([]); +export function setTray(tray) { + trays.update((current) => { + // Remove existing if it exists, else add + const index = current.findIndex(item => item.ID === tray.ID); + if ( index === -1 ) { + current.push(tray); + } else { + current[index] = tray; + } + return current; + }) +} +export function updateTrayLabel(tray) { + trays.update((current) => { + // Remove existing if it exists, else add + const index = current.findIndex(item => item.ID === tray.ID); + if ( index === -1 ) { + return log("ERROR: Attempted to update tray index ", tray.ID, "but it doesn't exist") + } + current[index].Label = tray.Label; + return current; + }) +} + +export function deleteTrayMenu(id) { + trays.update((current) => { + // Remove existing if it exists, else add + const index = current.findIndex(item => item.ID === id); + if ( index === -1 ) { + return log("ERROR: Attempted to delete tray index ", id, "but it doesn't exist") + } + current.splice(index, 1); + return current; + }) +} + +export let selectedMenu = writable(null); \ No newline at end of file diff --git a/v2/internal/runtime/js/runtime/src/websocket.js b/v2/internal/runtime/js/runtime/src/websocket.js new file mode 100644 index 000000000..a3e3aeb70 --- /dev/null +++ b/v2/internal/runtime/js/runtime/src/websocket.js @@ -0,0 +1,158 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + + +import {deleteTrayMenu, hideOverlay, setTray, showOverlay, updateTrayLabel} from './store'; +import {log} from './log'; + +let websocket = null; +let callback = null; +let connectTimer; + +export function StartWebsocket(userCallback) { + + callback = userCallback; + + window.onbeforeunload = function() { + if( websocket ) { + websocket.onclose = function () { }; + websocket.close(); + websocket = null; + } + }; + + // ...and attempt to connect + connect(); + +} + +function setupIPCBridge() { + window.wailsInvoke = (message) => { + websocket.send(message); + }; +} + +// Handles incoming websocket connections +function handleConnect() { + log('Connected to backend'); + setupIPCBridge(); + hideOverlay(); + clearInterval(connectTimer); + websocket.onclose = handleDisconnect; + websocket.onmessage = handleMessage; +} + +// Handles websocket disconnects +function handleDisconnect() { + log('Disconnected from backend'); + websocket = null; + showOverlay(); + connect(); +} + +// Try to connect to the backend every 1s (default value). +function connect() { + connectTimer = setInterval(function () { + if (websocket == null) { + websocket = new WebSocket('ws://' + window.location.hostname + ':34115/bridge'); + websocket.onopen = handleConnect; + websocket.onerror = function (e) { + e.stopImmediatePropagation(); + e.stopPropagation(); + e.preventDefault(); + websocket = null; + return false; + }; + } + }, 1000); +} + +// Adds a script to the Dom. +// Removes it if second parameter is true. +function addScript(script, remove) { + const s = document.createElement('script'); + s.setAttribute('type', 'text/javascript'); + s.textContent = script; + document.head.appendChild(s); + + // Remove internal messages from the DOM + if (remove) { + s.parentNode.removeChild(s); + } +} + +function handleMessage(message) { + // As a bridge we ignore js and css injections + switch (message.data[0]) { + // Wails library - inject! + case 'b': + message = message.data.slice(1); + addScript(message); + log('Loaded Wails Runtime'); + + // We need to now send a message to the backend telling it + // we have loaded (System Start) + window.wailsInvoke('SS'); + + // Now wails runtime is loaded, wails for the ready event + // and callback to the main app + // window.wails.Events.On('wails:loaded', function () { + if (callback) { + log('Notifying application'); + callback(window.wails); + } + // }); + break; + // Notifications + case 'n': + window.wails._.Notify(message.data.slice(1)); + break; + // // Binding + // case 'b': + // const binding = message.data.slice(1); + // //log("Binding: " + binding) + // window.wails._.NewBinding(binding); + // break; + // // Call back + case 'c': + const callbackData = message.data.slice(1); + window.wails._.Callback(callbackData); + break; + // Tray + case 'T': + const trayMessage = message.data.slice(1); + switch (trayMessage[0]) { + case 'S': + // Set tray + const trayJSON = trayMessage.slice(1); + let tray = JSON.parse(trayJSON); + setTray(tray); + break; + case 'U': + // Update label + const updateTrayLabelJSON = trayMessage.slice(1); + let trayLabelData = JSON.parse(updateTrayLabelJSON); + updateTrayLabel(trayLabelData); + break; + case 'D': + // Delete Tray Menu + const id = trayMessage.slice(1); + deleteTrayMenu(id); + break; + default: + log('Unknown tray message: ' + message.data); + } + break; + + default: + log('Unknown message: ' + message.data); + } +} diff --git a/v2/internal/runtime/js/server/darwin.js b/v2/internal/runtime/js/server/darwin.js new file mode 100644 index 000000000..c2af8faa7 --- /dev/null +++ b/v2/internal/runtime/js/server/darwin.js @@ -0,0 +1,17 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + +/** + * Initialises platform specific code + */ + +export function Init() { } diff --git a/v2/internal/runtime/js/server/ipc.js b/v2/internal/runtime/js/server/ipc.js new file mode 100644 index 000000000..1332dd2c1 --- /dev/null +++ b/v2/internal/runtime/js/server/ipc.js @@ -0,0 +1,52 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ +/* jshint esversion: 6 */ + +// IPC Listeners +var listeners = []; + +/** + * Adds a listener to IPC messages + * @param {function} callback + */ +export function AddIPCListener(callback) { + listeners.push(callback); +} + +/** + * Invoke sends the given message to the backend + * + * @param {string} message + */ +function Invoke(message) { + if (window.wailsbridge && window.wailsbridge.websocket) { + window.wailsbridge.websocket.send(JSON.stringify(message)); + } else { + console.log('Invoke called with: ' + message + ' but no runtime is available'); + } + + // Also send to listeners + if (listeners.length > 0) { + for (var i = 0; i < listeners.length; i++) { + listeners[i](message); + } + } +} + +/** + * Sends a message to the backend based on the given type, payload and callbackID + * + * @export + * @param {string} message + */ + +export function SendMessage(message) { + Invoke(message); +} diff --git a/v2/internal/runtime/js/server/linux.js b/v2/internal/runtime/js/server/linux.js new file mode 100644 index 000000000..718c01d8f --- /dev/null +++ b/v2/internal/runtime/js/server/linux.js @@ -0,0 +1,17 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +The electron alternative for Go +(c) Lea Anthony 2019-present +*/ + +/* jshint esversion: 6 */ + +/** + * Initialises platform specific code + */ + +export function Init() { } \ No newline at end of file diff --git a/v2/internal/runtime/js/webpack.config.js b/v2/internal/runtime/js/webpack.config.js new file mode 100644 index 000000000..62cb81a20 --- /dev/null +++ b/v2/internal/runtime/js/webpack.config.js @@ -0,0 +1,4 @@ +/* eslint-disable */ +module.exports = (env) => { + return require(`./webpack.${env}.js`); +}; \ No newline at end of file diff --git a/v2/internal/runtime/js/webpack.desktop.js b/v2/internal/runtime/js/webpack.desktop.js new file mode 100644 index 000000000..8ad1d61de --- /dev/null +++ b/v2/internal/runtime/js/webpack.desktop.js @@ -0,0 +1,51 @@ +/* eslint-disable */ + +const path = require('path'); + +const platform = process.env.WAILSPLATFORM; +if (!platform) { + console.error("FATAL: Environment variable WAILSPLATFORM not set!"); + process.exit(1); +} + +module.exports = { + entry: './core/desktop', + mode: 'production', + output: { + path: path.resolve(__dirname, '..', 'assets'), + filename: 'desktop_'+platform+'.js', + library: 'Wails' + }, + resolve: { + alias: { + ipc$: path.resolve(__dirname, 'desktop/ipc.js'), + platform$: path.resolve(__dirname, `desktop/${platform}.js`) + } + }, + module: { + rules: [ + { + test: /\.m?js$/, + exclude: /(node_modules|bower_components)/, + use: { + loader: 'babel-loader', + options: { + plugins: ['@babel/plugin-transform-object-assign'], + presets: [ + [ + '@babel/preset-env', + { + 'useBuiltIns': 'entry', + 'corejs': { + 'version': 3, + 'proposals': true + } + } + ] + ] + } + } + } + ] + } +}; diff --git a/v2/internal/runtime/js/webpack.server.js b/v2/internal/runtime/js/webpack.server.js new file mode 100644 index 000000000..58ed73b67 --- /dev/null +++ b/v2/internal/runtime/js/webpack.server.js @@ -0,0 +1,54 @@ +/* eslint-disable */ + +const path = require('path'); + +const platform = process.env.WAILSPLATFORM; +if (!platform) { + console.error("FATAL: Environment variable WAILSPLATFORM not set!"); + process.exit(1); +} + +module.exports = { + entry: './core/server', + mode: 'production', + output: { + path: path.resolve(__dirname, '..', 'assets'), + filename: 'server.js', + library: 'Wails' + }, + resolve: { + alias: { + ipc$: path.resolve(__dirname, 'server/ipc.js'), + platform$: path.resolve(__dirname, `server/${platform}.js`) + } + }, + module: { + rules: [ + { + test: /\.m?js$/, + include: [ + path.resolve(__dirname, "server"), + ], + exclude: /(node_modules|bower_components)/, + use: { + loader: 'babel-loader', + options: { + plugins: ['@babel/plugin-transform-object-assign'], + presets: [ + [ + '@babel/preset-env', + { + 'useBuiltIns': 'entry', + 'corejs': { + 'version': 3, + 'proposals': true + } + } + ] + ] + } + } + } + ] + } +}; diff --git a/v2/internal/runtime/scripts/rebuild.go b/v2/internal/runtime/scripts/rebuild.go new file mode 100644 index 000000000..b16fe3880 --- /dev/null +++ b/v2/internal/runtime/scripts/rebuild.go @@ -0,0 +1,85 @@ +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/internal/shell" +) + +func main() { + + sourceDir := fs.RelativePath("../../../internal/runtime/js") + + platforms := []string{"darwin", "windows", "linux"} + + for _, platform := range platforms { + println("Building JS Runtime for " + platform) + envvars := []string{"WAILSPLATFORM=" + platform} + + // Split up the InstallCommand and execute it + stdout, stderr, err := shell.RunCommand(sourceDir, "npm", "install") + if err != nil { + for _, l := range strings.Split(stdout, "\n") { + fmt.Printf(" %s\n", l) + } + for _, l := range strings.Split(stderr, "\n") { + fmt.Printf(" %s\n", l) + } + } + + runtimeDir := fs.RelativePath("../js") + cmd := shell.CreateCommand(runtimeDir, "npm", "run", "build:desktop") + cmd.Env = append(os.Environ(), envvars...) + var stdo, stde bytes.Buffer + cmd.Stdout = &stdo + cmd.Stderr = &stde + err = cmd.Run() + if err != nil { + for _, l := range strings.Split(stdo.String(), "\n") { + fmt.Printf(" %s\n", l) + } + for _, l := range strings.Split(stde.String(), "\n") { + fmt.Printf(" %s\n", l) + } + log.Fatal(err) + } + + wailsJS := fs.RelativePath("../assets/desktop_" + platform + ".js") + runtimeData, err := ioutil.ReadFile(wailsJS) + if err != nil { + log.Fatal(err) + } + + // Copy this file to bridge directory for embedding + bridgeDir := fs.RelativePath("../../bridge/" + platform + ".js") + println("Copying", wailsJS, "to", bridgeDir) + err = fs.CopyFile(wailsJS, bridgeDir) + if err != nil { + log.Fatal(err) + } + + // Convert to C structure + runtimeC := ` +// runtime.c (c) 2019-Present Lea Anthony. +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file was auto-generated. DO NOT MODIFY. +const unsigned char runtime[]={` + for _, b := range runtimeData { + runtimeC += fmt.Sprintf("0x%x, ", b) + } + runtimeC += "0x00};" + + // Save file + outputFile := fs.RelativePath(fmt.Sprintf("../../ffenestri/runtime_%s.c", platform)) + + if err := ioutil.WriteFile(outputFile, []byte(runtimeC), 0600); err != nil { + log.Fatal(err) + } + } +} diff --git a/v2/internal/servicebus/extract.go b/v2/internal/servicebus/extract.go new file mode 100644 index 000000000..bcd46186c --- /dev/null +++ b/v2/internal/servicebus/extract.go @@ -0,0 +1,17 @@ +package servicebus + +import ( + "context" + "log" + "runtime" +) + +func ExtractBus(ctx context.Context) *ServiceBus { + bus := ctx.Value("bus") + if bus == nil { + pc, _, _, _ := runtime.Caller(1) + funcName := runtime.FuncForPC(pc).Name() + log.Fatalf("cannot call '%s': Application not initialised", funcName) + } + return bus.(*ServiceBus) +} diff --git a/v2/internal/servicebus/message.go b/v2/internal/servicebus/message.go new file mode 100644 index 000000000..aea95f5e3 --- /dev/null +++ b/v2/internal/servicebus/message.go @@ -0,0 +1,43 @@ +package servicebus + +// Message is a service bus message that contains a +// topic and data +type Message struct { + topic string + data interface{} + target string +} + +// NewMessage creates a new message with the given +// topic and data +func NewMessage(topic string, data interface{}) *Message { + return &Message{ + topic: topic, + data: data, + } +} + +// NewMessageForTarget creates a new message with the given +// topic and data +func NewMessageForTarget(topic string, data interface{}, target string) *Message { + return &Message{ + topic: topic, + data: data, + target: target, + } +} + +// Topic returns the message topic +func (m *Message) Topic() string { + return m.topic +} + +// Data returns the message data +func (m *Message) Data() interface{} { + return m.data +} + +// Target returns the message Target +func (m *Message) Target() string { + return m.target +} diff --git a/v2/internal/servicebus/servicebus.go b/v2/internal/servicebus/servicebus.go new file mode 100644 index 000000000..86faa9aed --- /dev/null +++ b/v2/internal/servicebus/servicebus.go @@ -0,0 +1,181 @@ +package servicebus + +import ( + "context" + "fmt" + "strings" + "sync" + + "github.com/wailsapp/wails/v2/internal/logger" +) + +// ServiceBus is a messaging bus for Wails applications +type ServiceBus struct { + listeners map[string][]chan *Message + messageQueue chan *Message + lock sync.RWMutex + closed bool + debug bool + logger logger.CustomLogger + ctx context.Context + cancel context.CancelFunc +} + +// New creates a new ServiceBus +// The internal message queue is set to 100 messages +// Listener queues are set to 10 +func New(logger *logger.Logger) *ServiceBus { + + ctx, cancel := context.WithCancel(context.Background()) + return &ServiceBus{ + listeners: make(map[string][]chan *Message), + messageQueue: make(chan *Message, 100), + logger: logger.CustomLogger("Service Bus"), + ctx: ctx, + cancel: cancel, + } +} + +// dispatch the given message to the listeners +func (s *ServiceBus) dispatchMessage(message *Message) { + + // Lock to prevent additions to the listeners + s.lock.RLock() + defer s.lock.RUnlock() + + // Iterate over listener's topics + for topic := range s.listeners { + + // If the topic matches + if strings.HasPrefix(message.Topic(), topic) { + + // Iterate over the listeners + for _, callback := range s.listeners[topic] { + + // Process the message + callback <- message + } + } + } +} + +// Debug puts the service bus into debug mode. +func (s *ServiceBus) Debug() { + s.debug = true +} + +// Start the service bus +func (s *ServiceBus) Start() error { + + // Prevent starting when closed + if s.closed { + return fmt.Errorf("cannot call start on closed servicebus") + } + + s.logger.Trace("Starting") + + go func() { + defer s.logger.Trace("Stopped") + + // Loop until we get a quit message + for { + + select { + case <-s.ctx.Done(): + return + + // Listen for messages + case message := <-s.messageQueue: + + // Log message if in debug mode + if s.debug { + s.logger.Trace("Got message: { Topic: %s, Interface: %#v }", message.Topic(), message.Data()) + } + // Dispatch message + s.dispatchMessage(message) + } + } + + }() + + return nil +} + +// Stop the service bus +func (s *ServiceBus) Stop() error { + + // Prevent subscribing when closed + if s.closed { + return fmt.Errorf("cannot call stop on closed servicebus") + } + + s.closed = true + + // Send quit message + s.cancel() + + // Close down subscriber channels + s.lock.Lock() + defer s.lock.Unlock() + + for _, subscribers := range s.listeners { + for _, channel := range subscribers { + close(channel) + } + } + + // Close message queue + close(s.messageQueue) + + return nil +} + +// UnSubscribe removes the listeners for the given topic (Use with caution!) +func (s *ServiceBus) UnSubscribe(topic string) { + // Prevent any reads or writes to the listeners whilst + // we create a new one + s.lock.Lock() + defer s.lock.Unlock() + s.listeners[topic] = nil +} + +// Subscribe is used to register a listener's interest in a topic +func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) { + + // Prevent subscribing when closed + if s.closed { + return nil, fmt.Errorf("cannot call subscribe on closed servicebus") + } + + // Prevent any reads or writes to the listeners whilst + // we create a new one + s.lock.Lock() + defer s.lock.Unlock() + + // Append the new listener + listener := make(chan *Message, 10) + s.listeners[topic] = append(s.listeners[topic], listener) + return (<-chan *Message)(listener), nil + +} + +// Publish sends the given message on the service bus +func (s *ServiceBus) Publish(topic string, data interface{}) { + // Prevent publish when closed + if s.closed { + return + } + + message := NewMessage(topic, data) + s.messageQueue <- message +} + +// PublishForTarget sends the given message on the service bus for the given target +func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) { + // Prevent publish when closed + if s.closed { + return + } + message := NewMessageForTarget(topic, data, target) + s.messageQueue <- message +} diff --git a/v2/internal/servicebus/servicebus_test.go b/v2/internal/servicebus/servicebus_test.go new file mode 100644 index 000000000..750a56aaa --- /dev/null +++ b/v2/internal/servicebus/servicebus_test.go @@ -0,0 +1,230 @@ +package servicebus + +import ( + "sync" + "testing" + + "github.com/matryer/is" + "github.com/wailsapp/wails/v2/internal/logger" +) + +type Person interface { + FullName() string +} + +type person struct { + Firstname string + Lastname string +} + +func newPerson(firstname string, lastname string) *person { + result := &person{} + result.Firstname = firstname + result.Lastname = lastname + return result +} + +func (p *person) FullName() string { + return p.Firstname + " " + p.Lastname +} + +func TestSingleTopic(t *testing.T) { + + is := is.New(t) + + var expected string = "I am a message!" + var actual string + + var wg sync.WaitGroup + + // Create new bus + bus := New(logger.New()) + messageChannel, _ := bus.Subscribe("hello") + + wg.Add(1) + go func() { + message := <-messageChannel + actual = message.Data().(string) + wg.Done() + }() + + bus.Start() + bus.Publish("hello", "I am a message!") + wg.Wait() + bus.Stop() + + is.Equal(actual, expected) + +} +func TestMultipleTopics(t *testing.T) { + + is := is.New(t) + + var hello string + var world string + var expected string = "Hello World!" + + var wg sync.WaitGroup + + // Create new bus + bus := New(logger.New()) + + // Create subscriptions + helloChannel, _ := bus.Subscribe("hello") + worldChannel, _ := bus.Subscribe("world") + + wg.Add(1) + go func() { + counter := 2 + for counter > 0 { + select { + case helloMessage := <-helloChannel: + hello = helloMessage.Data().(string) + counter-- + case worldMessage := <-worldChannel: + world = worldMessage.Data().(string) + counter-- + } + } + wg.Done() + }() + + bus.Start() + bus.Publish("hello", "Hello ") + bus.Publish("world", "World!") + wg.Wait() + bus.Stop() + + is.Equal(hello+world, expected) +} + +func TestSingleTopicWildcard(t *testing.T) { + + is := is.New(t) + + var expected string = "I am a message!" + var actual string + + var wg sync.WaitGroup + + // Create new bus + bus := New(logger.New()) + messageChannel, _ := bus.Subscribe("hello") + + wg.Add(1) + go func() { + message := <-messageChannel + actual = message.Data().(string) + wg.Done() + }() + + bus.Start() + bus.Publish("hello:wildcard:test", "I am a message!") + wg.Wait() + bus.Stop() + + is.Equal(actual, expected) + +} +func TestMultipleTopicsWildcard(t *testing.T) { + + is := is.New(t) + + var hello string + var world string + var expected string = "Hello World!" + + var wg sync.WaitGroup + + // Create new bus + bus := New(logger.New()) + helloChannel, _ := bus.Subscribe("hello") + worldChannel, _ := bus.Subscribe("world") + + wg.Add(1) + go func() { + counter := 2 + for counter > 0 { + select { + case helloMessage := <-helloChannel: + hello = helloMessage.Data().(string) + counter-- + case worldMessage := <-worldChannel: + world = worldMessage.Data().(string) + counter-- + } + } + wg.Done() + }() + + bus.Start() + bus.Publish("hello:wildcard:test", "Hello ") + bus.Publish("world:wildcard:test", "World!") + wg.Wait() + bus.Stop() + + is.Equal(hello+world, expected) +} + +func TestStructData(t *testing.T) { + + is := is.New(t) + + var expected string = "Tom Jones" + var actual string + + var wg sync.WaitGroup + + // Create new bus + bus := New(logger.New()) + messageChannel, _ := bus.Subscribe("person") + + wg.Add(1) + go func() { + message := <-messageChannel + p := message.Data().(*person) + actual = p.FullName() + wg.Done() + }() + + bus.Start() + bus.Publish("person", newPerson("Tom", "Jones")) + wg.Wait() + bus.Stop() + + is.Equal(actual, expected) + +} + +func TestErrors(t *testing.T) { + + is := is.New(t) + + // Create new bus + bus := New(logger.New()) + + _, err := bus.Subscribe("person") + is.NoErr(err) + + err = bus.Start() + is.NoErr(err) + + err = bus.Publish("person", newPerson("Tom", "Jones")) + is.NoErr(err) + + err = bus.Stop() + is.NoErr(err) + + err = bus.Stop() + is.True(err != nil) + + err = bus.Start() + is.True(err != nil) + + _, err = bus.Subscribe("person") + is.True(err != nil) + + err = bus.Publish("person", newPerson("Tom", "Jones")) + is.True(err != nil) + +} diff --git a/v2/internal/shell/shell.go b/v2/internal/shell/shell.go new file mode 100644 index 000000000..8c36836fb --- /dev/null +++ b/v2/internal/shell/shell.go @@ -0,0 +1,90 @@ +package shell + +import ( + "bytes" + "os" + "os/exec" +) + +type Command struct { + command string + args []string + env []string + dir string + stdo, stde bytes.Buffer +} + +func NewCommand(command string) *Command { + return &Command{ + command: command, + env: os.Environ(), + } +} + +func (c *Command) Dir(dir string) { + c.dir = dir +} + +func (c *Command) Env(name string, value string) { + c.env = append(c.env, name+"="+value) +} + +func (c *Command) Run() error { + cmd := exec.Command(c.command, c.args...) + if c.dir != "" { + cmd.Dir = c.dir + } + cmd.Stdout = &c.stdo + cmd.Stderr = &c.stde + return cmd.Run() +} + +func (c *Command) Stdout() string { + return c.stdo.String() +} +func (c *Command) Stderr() string { + return c.stde.String() +} + +func (c *Command) AddArgs(args []string) { + for _, arg := range args { + c.args = append(c.args, arg) + } +} + +// CreateCommand returns a *Cmd struct that when run, will run the given command + args in the given directory +func CreateCommand(directory string, command string, args ...string) *exec.Cmd { + cmd := exec.Command(command, args...) + cmd.Dir = directory + return cmd +} + +// RunCommand will run the given command + args in the given directory +// Will return stdout, stderr and error +func RunCommand(directory string, command string, args ...string) (string, string, error) { + cmd := CreateCommand(directory, command, args...) + var stdo, stde bytes.Buffer + cmd.Stdout = &stdo + cmd.Stderr = &stde + err := cmd.Run() + return stdo.String(), stde.String(), err +} + +// RunCommandVerbose will run the given command + args in the given directory +// Will return an error if one occurs +func RunCommandVerbose(directory string, command string, args ...string) error { + cmd := CreateCommand(directory, command, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + return err +} + +// CommandExists returns true if the given command can be found on the shell +func CommandExists(name string) bool { + _, err := exec.LookPath(name) + if err != nil { + return false + } + return true +} diff --git a/v2/internal/signal/signal.go b/v2/internal/signal/signal.go new file mode 100644 index 000000000..6d38d89c9 --- /dev/null +++ b/v2/internal/signal/signal.go @@ -0,0 +1,73 @@ +package signal + +import ( + "context" + "os" + gosignal "os/signal" + "sync" + "syscall" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// Manager manages signals such as CTRL-C +type Manager struct { + // Service Bus + bus *servicebus.ServiceBus + + // logger + logger logger.CustomLogger + + // signalChannel + signalchannel chan os.Signal + + // ctx + ctx context.Context + cancel context.CancelFunc + + // Parent waitgroup + wg *sync.WaitGroup +} + +// NewManager creates a new signal manager +func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) { + + result := &Manager{ + bus: bus, + logger: logger.CustomLogger("Event Manager"), + signalchannel: make(chan os.Signal, 2), + ctx: ctx, + cancel: cancel, + wg: ctx.Value("waitgroup").(*sync.WaitGroup), + } + + return result, nil +} + +// Start the Signal Manager +func (m *Manager) Start() { + + // Hook into interrupts + gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM) + + m.wg.Add(1) + + // Spin off signal listener and wait for either a cancellation + // or signal + go func() { + select { + case <-m.signalchannel: + println() + m.logger.Trace("Ctrl+C detected. Shutting down...") + m.bus.Publish("quit", "ctrl-c pressed") + + // Start shutdown of Wails + m.cancel() + + case <-m.ctx.Done(): + } + m.logger.Trace("Shutdown") + m.wg.Done() + }() +} diff --git a/v2/internal/subsystem/binding.go b/v2/internal/subsystem/binding.go new file mode 100644 index 000000000..cfc4bae4f --- /dev/null +++ b/v2/internal/subsystem/binding.go @@ -0,0 +1,64 @@ +package subsystem + +import ( + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// Binding is the Binding subsystem. It manages all service bus messages +// starting with "binding". +type Binding struct { + bindingChannel <-chan *servicebus.Message + + running bool + + // Binding db + bindings *binding.Bindings + + // logger + logger logger.CustomLogger +} + +// NewBinding creates a new binding subsystem. Uses the given bindings db for reference. +func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings) (*Binding, error) { + + // Subscribe to event messages + bindingChannel, err := bus.Subscribe("binding") + if err != nil { + return nil, err + } + + result := &Binding{ + bindingChannel: bindingChannel, + logger: logger.CustomLogger("Binding Subsystem"), + bindings: bindings, + } + + return result, nil +} + +// Start the subsystem +func (b *Binding) Start() error { + + b.running = true + + b.logger.Trace("Starting") + + // Spin off a go routine + go func() { + for b.running { + select { + case bindingMessage := <-b.bindingChannel: + b.logger.Trace("Got binding message: %+v", bindingMessage) + } + } + b.logger.Trace("Shutdown") + }() + + return nil +} + +func (b *Binding) Close() { + b.running = false +} diff --git a/v2/internal/subsystem/call.go b/v2/internal/subsystem/call.go new file mode 100644 index 000000000..f9ee4fd47 --- /dev/null +++ b/v2/internal/subsystem/call.go @@ -0,0 +1,202 @@ +package subsystem + +import ( + "context" + "encoding/json" + "fmt" + "github.com/wailsapp/wails/v2/pkg/runtime" + "strings" + "sync" + + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher/message" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// Call is the Call subsystem. It manages all service bus messages +// starting with "call". +type Call struct { + callChannel <-chan *servicebus.Message + + // quit flag + shouldQuit bool + + // bindings DB + DB *binding.DB + + // ServiceBus + bus *servicebus.ServiceBus + + // logger + logger logger.CustomLogger + + // context + ctx context.Context + + // parent waitgroup + wg *sync.WaitGroup +} + +// NewCall creates a new call subsystem +func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB) (*Call, error) { + + // Subscribe to event messages + callChannel, err := bus.Subscribe("call:invoke") + if err != nil { + return nil, err + } + + result := &Call{ + callChannel: callChannel, + logger: logger.CustomLogger("Call Subsystem"), + DB: DB, + bus: bus, + ctx: ctx, + wg: ctx.Value("waitgroup").(*sync.WaitGroup), + } + + return result, nil +} + +// Start the subsystem +func (c *Call) Start() error { + + c.wg.Add(1) + + // Spin off a go routine + go func() { + defer c.logger.Trace("Shutdown") + for { + select { + case <-c.ctx.Done(): + c.wg.Done() + return + case callMessage := <-c.callChannel: + c.processCall(callMessage) + } + } + + }() + + return nil +} + +func (c *Call) processCall(callMessage *servicebus.Message) { + + c.logger.Trace("Got message: %+v", callMessage) + + // Extract payload + payload := callMessage.Data().(*message.CallMessage) + + // Lookup method + registeredMethod := c.DB.GetMethod(payload.Name) + + // Check if it's a system call + if strings.HasPrefix(payload.Name, ".wails.") { + c.processSystemCall(payload, callMessage.Target()) + return + } + + // Check we have it + if registeredMethod == nil { + c.sendError(fmt.Errorf("Method not registered"), payload, callMessage.Target()) + return + } + c.logger.Trace("Got registered method: %+v", registeredMethod) + + args, err := registeredMethod.ParseArgs(payload.Args) + if err != nil { + c.sendError(fmt.Errorf("Error parsing arguments: %s", err.Error()), payload, callMessage.Target()) + } + + result, err := registeredMethod.Call(args) + if err != nil { + c.sendError(err, payload, callMessage.Target()) + return + } + c.logger.Trace("registeredMethod.Call: %+v, %+v", result, err) + // process result + c.sendResult(result, payload, callMessage.Target()) + +} + +func (c *Call) processSystemCall(payload *message.CallMessage, clientID string) { + c.logger.Trace("Got internal System call: %+v", payload) + callName := strings.TrimPrefix(payload.Name, ".wails.") + switch callName { + case "Dialog.Open": + var dialogOptions runtime.OpenDialogOptions + err := json.Unmarshal(payload.Args[0], &dialogOptions) + if err != nil { + c.logger.Error("Error decoding: %s", err) + } + result, err := runtime.OpenFileDialog(c.ctx, dialogOptions) + if err != nil { + c.logger.Error("Error: %s", err) + } + c.sendResult(result, payload, clientID) + case "Dialog.Save": + var dialogOptions runtime.SaveDialogOptions + err := json.Unmarshal(payload.Args[0], &dialogOptions) + if err != nil { + c.logger.Error("Error decoding: %s", err) + } + result, err := runtime.SaveFileDialog(c.ctx, dialogOptions) + if err != nil { + c.logger.Error("Error: %s", err) + } + c.sendResult(result, payload, clientID) + case "Dialog.Message": + var dialogOptions runtime.MessageDialogOptions + err := json.Unmarshal(payload.Args[0], &dialogOptions) + if err != nil { + c.logger.Error("Error decoding: %s", err) + } + result, err := runtime.MessageDialog(c.ctx, dialogOptions) + if err != nil { + c.logger.Error("Error: %s", err) + } + c.sendResult(result, payload, clientID) + default: + c.logger.Error("Unknown system call: %+v", callName) + } +} + +func (c *Call) sendResult(result interface{}, payload *message.CallMessage, clientID string) { + c.logger.Trace("Sending success result with CallbackID '%s' : %+v\n", payload.CallbackID, result) + incomingMessage := &CallbackMessage{ + Result: result, + CallbackID: payload.CallbackID, + } + messageData, err := json.Marshal(incomingMessage) + c.logger.Trace("json incomingMessage data: %+v\n", string(messageData)) + if err != nil { + // what now? + c.logger.Fatal(err.Error()) + } + c.bus.PublishForTarget("call:result", string(messageData), clientID) +} + +func (c *Call) sendError(err error, payload *message.CallMessage, clientID string) { + c.logger.Trace("Sending error result with CallbackID '%s' : %+v\n", payload.CallbackID, err.Error()) + incomingMessage := &CallbackMessage{ + Err: err.Error(), + CallbackID: payload.CallbackID, + } + + messageData, err := json.Marshal(incomingMessage) + c.logger.Trace("json incomingMessage data: %+v\n", string(messageData)) + if err != nil { + // what now? + c.logger.Fatal(err.Error()) + } + c.bus.PublishForTarget("call:result", string(messageData), clientID) +} + +// CallbackMessage defines a message that contains the result of a call +type CallbackMessage struct { + Result interface{} `json:"result"` + Err string `json:"error"` + CallbackID string `json:"callbackid"` +} diff --git a/v2/internal/subsystem/event.go b/v2/internal/subsystem/event.go new file mode 100644 index 000000000..988bec7dd --- /dev/null +++ b/v2/internal/subsystem/event.go @@ -0,0 +1,191 @@ +package subsystem + +import ( + "context" + "strings" + "sync" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher/message" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// eventListener holds a callback function which is invoked when +// the event listened for is emitted. It has a counter which indicates +// how the total number of events it is interested in. A value of zero +// means it does not expire (default). +type eventListener struct { + callback func(...interface{}) // Function to call with emitted event data + counter int // The number of times this callback may be called. -1 = infinite + delete bool // Flag to indicate that this listener should be deleted +} + +// Event is the Eventing subsystem. It manages all service bus messages +// starting with "event". +type Event struct { + eventChannel <-chan *servicebus.Message + + // Event listeners + listeners map[string][]*eventListener + notifyLock sync.RWMutex + + // logger + logger logger.CustomLogger + + // ctx + ctx context.Context + + // parent waitgroup + wg *sync.WaitGroup +} + +// NewEvent creates a new log subsystem +func NewEvent(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) { + + // Subscribe to event messages + eventChannel, err := bus.Subscribe("event") + if err != nil { + return nil, err + } + + result := &Event{ + eventChannel: eventChannel, + logger: logger.CustomLogger("Event Subsystem"), + listeners: make(map[string][]*eventListener), + ctx: ctx, + wg: ctx.Value("waitgroup").(*sync.WaitGroup), + } + + return result, nil +} + +// RegisterListener provides a means of subscribing to events of type "eventName" +func (e *Event) RegisterListener(eventName string, callback func(...interface{}), counter int) { + + // Create new eventListener + thisListener := &eventListener{ + callback: callback, + counter: counter, + delete: false, + } + + e.notifyLock.Lock() + // Append the new listener to the listeners slice + e.listeners[eventName] = append(e.listeners[eventName], thisListener) + e.notifyLock.Unlock() +} + +// Start the subsystem +func (e *Event) Start() error { + + e.logger.Trace("Starting") + + e.wg.Add(1) + + // Spin off a go routine + go func() { + defer e.logger.Trace("OnShutdown") + for { + select { + case <-e.ctx.Done(): + e.wg.Done() + return + case eventMessage := <-e.eventChannel: + splitTopic := strings.Split(eventMessage.Topic(), ":") + eventType := splitTopic[1] + switch eventType { + case "emit": + if len(splitTopic) != 4 { + e.logger.Error("Received emit message with invalid topic format. Expected 4 sections in topic, got %s", splitTopic) + continue + } + eventSource := splitTopic[3] + e.logger.Trace("Got Event Message: %s %+v", eventMessage.Topic(), eventMessage.Data()) + event := eventMessage.Data().(*message.EventMessage) + eventName := event.Name + switch eventSource { + + case "j": + // Notify Go Subscribers + e.logger.Trace("Notify Go subscribers to event '%s'", eventName) + go e.notifyListeners(eventName, event) + case "g": + // Notify Go listeners + e.logger.Trace("Got Go Event: %s", eventName) + go e.notifyListeners(eventName, event) + default: + e.logger.Error("unknown emit event message: %+v", eventMessage) + } + case "on": + // We wish to subscribe to an event channel + var message *message.OnEventMessage = eventMessage.Data().(*message.OnEventMessage) + eventName := message.Name + callback := message.Callback + e.RegisterListener(eventName, callback, message.Counter) + e.logger.Trace("Registered listener for event '%s' with callback %p", eventName, callback) + default: + e.logger.Error("unknown event message: %+v", eventMessage) + } + } + } + + }() + + return nil +} + +// Notifies listeners for the given event name +func (e *Event) notifyListeners(eventName string, message *message.EventMessage) { + + // Get list of event listeners + listeners := e.listeners[eventName] + if listeners == nil { + e.logger.Trace("No listeners for event '%s'", eventName) + return + } + + // Lock the listeners + e.notifyLock.Lock() + + // We have a dirty flag to indicate that there are items to delete + itemsToDelete := false + + // Callback in goroutine + for _, listener := range listeners { + if listener.counter > 0 { + listener.counter-- + } + + go listener.callback(message.Data...) + + if listener.counter == 0 { + listener.delete = true + itemsToDelete = true + } + } + + // Do we have items to delete? + if itemsToDelete == true { + + // Create a new Listeners slice + var newListeners = []*eventListener{} + + // Iterate over current listeners + for _, listener := range listeners { + // If we aren't deleting the listener, add it to the new list + if !listener.delete { + newListeners = append(newListeners, listener) + } + } + + // Save new listeners or remove entry + if len(newListeners) > 0 { + e.listeners[eventName] = newListeners + } else { + delete(e.listeners, eventName) + } + } + + // Unlock + e.notifyLock.Unlock() +} diff --git a/v2/internal/subsystem/event_test.go b/v2/internal/subsystem/event_test.go new file mode 100644 index 000000000..99219948c --- /dev/null +++ b/v2/internal/subsystem/event_test.go @@ -0,0 +1,50 @@ +package subsystem + +import ( + "os" + "sync" + "testing" + + "github.com/matryer/is" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher/message" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +func TestSingleTopic(t *testing.T) { + + is := is.New(t) + + var expected string = "I am a message!" + var actual string + + var wg sync.WaitGroup + + // Create new bus + myLogger := logger.New(os.Stdout) + myLogger.SetLogLevel(logger.TRACE) + bus := servicebus.New(myLogger) + eventSubsystem, _ := NewEvent(bus, myLogger) + eventSubsystem.Start() + + eventSubsystem.RegisterListener("test", func(data ...interface{}) { + is.Equal(len(data), 1) + actual = data[0].(string) + wg.Done() + }) + + wg.Add(1) + + eventMessage := &message.EventMessage{ + Name: "test", + Data: []interface{}{"I am a message!"}, + } + + bus.Start() + bus.Publish("event:test:from:j", eventMessage) + wg.Wait() + bus.Stop() + + is.Equal(actual, expected) + +} diff --git a/v2/internal/subsystem/log.go b/v2/internal/subsystem/log.go new file mode 100644 index 000000000..36f5abfd6 --- /dev/null +++ b/v2/internal/subsystem/log.go @@ -0,0 +1,111 @@ +package subsystem + +import ( + "context" + "strconv" + "strings" + "sync" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// Log is the Logging subsystem. It handles messages with topics starting +// with "log:" +type Log struct { + logChannel <-chan *servicebus.Message + + // quit flag + shouldQuit bool + + // Logger! + logger *logger.Logger + + // Context for shutdown + ctx context.Context + cancel context.CancelFunc + + // internal waitgroup + wg sync.WaitGroup +} + +// NewLog creates a new log subsystem +func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger) (*Log, error) { + + // Subscribe to log messages + logChannel, err := bus.Subscribe("log") + if err != nil { + return nil, err + } + + ctx, cancel := context.WithCancel(context.Background()) + + result := &Log{ + logChannel: logChannel, + logger: logger, + ctx: ctx, + cancel: cancel, + } + + return result, nil +} + +// Start the subsystem +func (l *Log) Start() error { + + l.wg.Add(1) + + // Spin off a go routine + go func() { + defer l.logger.Trace("Logger Shutdown") + + for l.shouldQuit == false { + select { + case <-l.ctx.Done(): + l.wg.Done() + return + case logMessage := <-l.logChannel: + logType := strings.TrimPrefix(logMessage.Topic(), "log:") + switch logType { + case "print": + l.logger.Print(logMessage.Data().(string)) + case "trace": + l.logger.Trace(logMessage.Data().(string)) + case "debug": + l.logger.Debug(logMessage.Data().(string)) + case "info": + l.logger.Info(logMessage.Data().(string)) + case "warning": + l.logger.Warning(logMessage.Data().(string)) + case "error": + l.logger.Error(logMessage.Data().(string)) + case "fatal": + l.logger.Fatal(logMessage.Data().(string)) + case "setlevel": + switch inLevel := logMessage.Data().(type) { + case logger.LogLevel: + l.logger.SetLogLevel(inLevel) + case string: + uint64level, err := strconv.ParseUint(inLevel, 10, 8) + if err != nil { + l.logger.Error("Error parsing log level: %+v", inLevel) + continue + } + level := logger.LogLevel(uint64level) + l.logger.SetLogLevel(level) + } + + default: + l.logger.Error("unknown log message: %+v", logMessage) + } + } + } + }() + + return nil +} + +func (l *Log) Close() { + l.cancel() + l.wg.Wait() +} diff --git a/v2/internal/subsystem/menu.go b/v2/internal/subsystem/menu.go new file mode 100644 index 000000000..2a6552769 --- /dev/null +++ b/v2/internal/subsystem/menu.go @@ -0,0 +1,176 @@ +package subsystem + +import ( + "context" + "encoding/json" + "strings" + "sync" + + "github.com/wailsapp/wails/v2/pkg/menu" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/menumanager" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// Menu is the subsystem that handles the operation of menus. It manages all service bus messages +// starting with "menu". +type Menu struct { + menuChannel <-chan *servicebus.Message + + // shutdown flag + shouldQuit bool + + // logger + logger logger.CustomLogger + + // Service Bus + bus *servicebus.ServiceBus + + // Menu Manager + menuManager *menumanager.Manager + + // ctx + ctx context.Context + + // parent waitgroup + wg *sync.WaitGroup +} + +// NewMenu creates a new menu subsystem +func NewMenu(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *menumanager.Manager) (*Menu, error) { + + // Subscribe to menu messages + menuChannel, err := bus.Subscribe("menu:") + if err != nil { + return nil, err + } + + result := &Menu{ + menuChannel: menuChannel, + logger: logger.CustomLogger("Menu Subsystem"), + bus: bus, + menuManager: menuManager, + ctx: ctx, + wg: ctx.Value("waitgroup").(*sync.WaitGroup), + } + + return result, nil +} + +// Start the subsystem +func (m *Menu) Start() error { + + m.logger.Trace("Starting") + + m.wg.Add(1) + + // Spin off a go routine + go func() { + defer m.logger.Trace("Shutdown") + for { + select { + case <-m.ctx.Done(): + m.wg.Done() + return + case menuMessage := <-m.menuChannel: + splitTopic := strings.Split(menuMessage.Topic(), ":") + menuMessageType := splitTopic[1] + switch menuMessageType { + case "ontrayopen": + trayID := menuMessage.Data().(string) + m.menuManager.OnTrayMenuOpen(trayID) + case "ontrayclose": + trayID := menuMessage.Data().(string) + m.menuManager.OnTrayMenuClose(trayID) + case "clicked": + if len(splitTopic) != 2 { + m.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic) + continue + } + m.logger.Trace("Got Menu clicked Message: %s %+v", menuMessage.Topic(), menuMessage.Data()) + + type ClickCallbackMessage struct { + MenuItemID string `json:"menuItemID"` + MenuType string `json:"menuType"` + Data string `json:"data"` + ParentID string `json:"parentID"` + } + + var callbackData ClickCallbackMessage + payload := []byte(menuMessage.Data().(string)) + err := json.Unmarshal(payload, &callbackData) + if err != nil { + m.logger.Error("%s", err.Error()) + return + } + + err = m.menuManager.ProcessClick(callbackData.MenuItemID, callbackData.Data, callbackData.MenuType, callbackData.ParentID) + if err != nil { + m.logger.Trace("%s", err.Error()) + } + + // Make sure we catch any menu updates + case "updateappmenu": + updatedMenu, err := m.menuManager.UpdateApplicationMenu() + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } + + // Notify frontend of menu change + m.bus.Publish("menufrontend:updateappmenu", updatedMenu) + + case "updatecontextmenu": + contextMenu := menuMessage.Data().(*menu.ContextMenu) + updatedMenu, err := m.menuManager.UpdateContextMenu(contextMenu) + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } + + // Notify frontend of menu change + m.bus.Publish("menufrontend:updatecontextmenu", updatedMenu) + + case "settraymenu": + trayMenu := menuMessage.Data().(*menu.TrayMenu) + updatedMenu, err := m.menuManager.SetTrayMenu(trayMenu) + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } + + // Notify frontend of menu change + m.bus.Publish("menufrontend:settraymenu", updatedMenu) + + case "deletetraymenu": + trayMenu := menuMessage.Data().(*menu.TrayMenu) + trayID, err := m.menuManager.GetTrayID(trayMenu) + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } + + // Notify frontend of menu change + m.bus.Publish("menufrontend:deletetraymenu", trayID) + + case "updatetraymenulabel": + trayMenu := menuMessage.Data().(*menu.TrayMenu) + updatedLabel, err := m.menuManager.UpdateTrayMenuLabel(trayMenu) + if err != nil { + m.logger.Trace("%s", err.Error()) + return + } + + // Notify frontend of menu change + m.bus.Publish("menufrontend:updatetraymenulabel", updatedLabel) + + default: + m.logger.Error("unknown menu message: %+v", menuMessage) + } + } + } + }() + + return nil +} diff --git a/v2/internal/subsystem/runtime.go b/v2/internal/subsystem/runtime.go new file mode 100644 index 000000000..1719de96f --- /dev/null +++ b/v2/internal/subsystem/runtime.go @@ -0,0 +1,103 @@ +package subsystem + +import ( + "context" + "fmt" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/servicebus" + "strings" + "sync" +) + +// Runtime is the Runtime subsystem. It handles messages with topics starting +// with "runtime:" +type Runtime struct { + runtimeChannel <-chan *servicebus.Message + + // The hooks channel allows us to hook into frontend startup + hooksChannel <-chan *servicebus.Message + startupCallback func(ctx context.Context) + shutdownCallback func() + + // quit flag + shouldQuit bool + + logger logger.CustomLogger + + //ctx + ctx context.Context + + // OnStartup Hook + startupOnce sync.Once + + // Service bus + bus *servicebus.ServiceBus +} + +// NewRuntime creates a new runtime subsystem +func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(context.Context)) (*Runtime, error) { + + // Subscribe to log messages + runtimeChannel, err := bus.Subscribe("runtime:") + if err != nil { + return nil, err + } + + // Subscribe to log messages + hooksChannel, err := bus.Subscribe("hooks:") + if err != nil { + return nil, err + } + + result := &Runtime{ + runtimeChannel: runtimeChannel, + hooksChannel: hooksChannel, + logger: logger.CustomLogger("Runtime Subsystem"), + startupCallback: startupCallback, + bus: bus, + } + result.ctx = context.WithValue(ctx, "bus", bus) + + return result, nil +} + +// Start the subsystem +func (r *Runtime) Start() error { + + // Spin off a go routine + go func() { + defer r.logger.Trace("OnShutdown") + for { + select { + case hooksMessage := <-r.hooksChannel: + r.logger.Trace(fmt.Sprintf("Received hooksmessage: %+v", hooksMessage)) + messageSlice := strings.Split(hooksMessage.Topic(), ":") + hook := messageSlice[1] + switch hook { + case "startup": + if r.startupCallback != nil { + r.startupOnce.Do(func() { + go func() { + r.startupCallback(r.ctx) + // If we got a url, publish it now startup completed + url, ok := hooksMessage.Data().(string) + if ok && len(url) > 0 { + r.bus.Publish("url:handler", url) + } + }() + }) + } else { + r.logger.Warning("no startup callback registered!") + } + default: + r.logger.Error("unknown hook message: %+v", hooksMessage) + continue + } + case <-r.ctx.Done(): + return + } + } + }() + + return nil +} diff --git a/v2/internal/subsystem/url.go b/v2/internal/subsystem/url.go new file mode 100644 index 000000000..83cde87ce --- /dev/null +++ b/v2/internal/subsystem/url.go @@ -0,0 +1,98 @@ +package subsystem + +import ( + "context" + "strings" + "sync" + + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/servicebus" +) + +// URL is the URL Handler subsystem. It handles messages with topics starting +// with "url:" +type URL struct { + urlChannel <-chan *servicebus.Message + + // quit flag + shouldQuit bool + + // Logger! + logger *logger.Logger + + // Context for shutdown + ctx context.Context + cancel context.CancelFunc + + // internal waitgroup + wg sync.WaitGroup + + // Handlers + handlers map[string]func(string) +} + +// NewURL creates a new log subsystem +func NewURL(bus *servicebus.ServiceBus, logger *logger.Logger, handlers map[string]func(string)) (*URL, error) { + + // Subscribe to log messages + urlChannel, err := bus.Subscribe("url") + if err != nil { + return nil, err + } + + ctx, cancel := context.WithCancel(context.Background()) + + result := &URL{ + urlChannel: urlChannel, + logger: logger, + ctx: ctx, + cancel: cancel, + handlers: handlers, + } + + return result, nil +} + +// Start the subsystem +func (u *URL) Start() error { + + u.wg.Add(1) + + // Spin off a go routine + go func() { + defer u.logger.Trace("URL Shutdown") + + for u.shouldQuit == false { + select { + case <-u.ctx.Done(): + u.wg.Done() + return + case urlMessage := <-u.urlChannel: + // Guard against nil messages + if urlMessage == nil { + continue + } + messageType := strings.TrimPrefix(urlMessage.Topic(), "url:") + switch messageType { + case "handler": + url := urlMessage.Data().(string) + splitURL := strings.Split(url, ":") + protocol := splitURL[0] + callback, ok := u.handlers[protocol] + if ok { + go callback(url) + } + default: + u.logger.Error("unknown url message: %+v", urlMessage) + } + } + } + }() + + return nil +} + +func (u *URL) Close() { + u.cancel() + u.wg.Wait() +} diff --git a/v2/internal/system/operatingsystem/os.go b/v2/internal/system/operatingsystem/os.go new file mode 100644 index 000000000..39f1de8e0 --- /dev/null +++ b/v2/internal/system/operatingsystem/os.go @@ -0,0 +1,13 @@ +package operatingsystem + +// OS contains information about the operating system +type OS struct { + ID string + Name string + Version string +} + +// Info retrieves information about the current platform +func Info() (*OS, error) { + return platformInfo() +} diff --git a/v2/internal/system/operatingsystem/os_darwin.go b/v2/internal/system/operatingsystem/os_darwin.go new file mode 100644 index 000000000..adc808773 --- /dev/null +++ b/v2/internal/system/operatingsystem/os_darwin.go @@ -0,0 +1,48 @@ +package operatingsystem + +import ( + "github.com/wailsapp/wails/v2/internal/shell" + "strings" +) + +func getSysctlValue(key string) (string, error) { + stdout, _, err := shell.RunCommand(".", "sysctl", key) + if err != nil { + return "", err + } + version := strings.TrimPrefix(stdout, key+": ") + return strings.TrimSpace(version), nil +} + +func platformInfo() (*OS, error) { + // Default value + var result OS + result.ID = "Unknown" + result.Name = "MacOS" + result.Version = "Unknown" + + version, err := getSysctlValue("kern.osproductversion") + if err != nil { + return nil, err + } + result.Version = version + ID, err := getSysctlValue("kern.osrevision") + if err != nil { + return nil, err + } + result.ID = ID + + // cmd := CreateCommand(directory, command, args...) + // var stdo, stde bytes.Buffer + // cmd.Stdout = &stdo + // cmd.Stderr = &stde + // err := cmd.Run() + // return stdo.String(), stde.String(), err + // } + // sysctl := shell.NewCommand("sysctl") + // kern.ostype: Darwin + // kern.osrelease: 20.1.0 + // kern.osrevision: 199506 + + return &result, nil +} diff --git a/v2/internal/system/operatingsystem/os_linux.go b/v2/internal/system/operatingsystem/os_linux.go new file mode 100644 index 000000000..090bba2ac --- /dev/null +++ b/v2/internal/system/operatingsystem/os_linux.go @@ -0,0 +1,51 @@ +// +build linux + +package operatingsystem + +import ( + "fmt" + "io/ioutil" + "os" + "strings" +) + +// platformInfo is the platform specific method to get system information +func platformInfo() (*OS, error) { + _, err := os.Stat("/etc/os-release") + if os.IsNotExist(err) { + return nil, fmt.Errorf("unable to read system information") + } + + osRelease, _ := ioutil.ReadFile("/etc/os-release") + return parseOsRelease(string(osRelease)), nil +} + +func parseOsRelease(osRelease string) *OS { + + // Default value + var result OS + result.ID = "Unknown" + result.Name = "Unknown" + result.Version = "Unknown" + + // Split into lines + lines := strings.Split(osRelease, "\n") + // Iterate lines + for _, line := range lines { + // Split each line by the equals char + splitLine := strings.SplitN(line, "=", 2) + // Check we have + if len(splitLine) != 2 { + continue + } + switch splitLine[0] { + case "ID": + result.ID = strings.ToLower(strings.Trim(splitLine[1], "\"")) + case "NAME": + result.Name = strings.Trim(splitLine[1], "\"") + case "VERSION_ID": + result.Version = strings.Trim(splitLine[1], "\"") + } + } + return &result +} diff --git a/v2/internal/system/operatingsystem/os_windows.go b/v2/internal/system/operatingsystem/os_windows.go new file mode 100644 index 000000000..0f294ba9c --- /dev/null +++ b/v2/internal/system/operatingsystem/os_windows.go @@ -0,0 +1,30 @@ +package operatingsystem + +import ( + "fmt" + + "golang.org/x/sys/windows/registry" +) + +func platformInfo() (*OS, error) { + // Default value + var result OS + result.ID = "Unknown" + result.Name = "Windows" + result.Version = "Unknown" + + // Credit: https://stackoverflow.com/a/33288328 + // Ignore errors as it isn't a showstopper + key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + + productName, _, _ := key.GetStringValue("ProductName") + currentBuild, _, _ := key.GetStringValue("CurrentBuildNumber") + displayVersion, _, _ := key.GetStringValue("DisplayVersion") + releaseId, _, _ := key.GetStringValue("ReleaseId") + + result.Name = productName + result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild) + result.ID = displayVersion + + return &result, key.Close() +} diff --git a/v2/internal/system/packagemanager/apt.go b/v2/internal/system/packagemanager/apt.go new file mode 100644 index 000000000..cc37e55bb --- /dev/null +++ b/v2/internal/system/packagemanager/apt.go @@ -0,0 +1,97 @@ +// +build linux + +package packagemanager + +import ( + "regexp" + "strings" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +// Apt represents the Apt manager +type Apt struct { + name string + osid string +} + +// NewApt creates a new Apt instance +func NewApt(osid string) *Apt { + return &Apt{ + name: "apt", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (a *Apt) Packages() packagemap { + return packagemap{ + "libgtk-3": []*Package{ + {Name: "libgtk-3-dev", SystemPackage: true, Library: true}, + }, + "libwebkit": []*Package{ + {Name: "libwebkit2gtk-4.0-dev", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "build-essential", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm", SystemPackage: true}, + }, + "docker": []*Package{ + {Name: "docker.io", SystemPackage: true, Optional: true}, + }, + } +} + +// Name returns the name of the package manager +func (a *Apt) Name() string { + return a.name +} + +// PackageInstalled tests if the given package name is installed +func (a *Apt) PackageInstalled(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "apt", "-qq", "list", pkg.Name) + return strings.Contains(stdout, "[installed]"), err +} + +// PackageAvailable tests if the given package is available for installation +func (a *Apt) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "apt", "-qq", "list", pkg.Name) + // We add a space to ensure we get a full match, not partial match + output := a.removeEscapeSequences(stdout) + installed := strings.HasPrefix(output, pkg.Name) + a.getPackageVersion(pkg, output) + return installed, err +} + +// InstallCommand returns the package manager specific command to install a package +func (a *Apt) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand[a.osid] + } + return "sudo apt install " + pkg.Name +} + +func (a *Apt) removeEscapeSequences(in string) string { + escapechars, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`) + return escapechars.ReplaceAllString(in, "") +} + +func (a *Apt) getPackageVersion(pkg *Package, output string) { + + splitOutput := strings.Split(output, " ") + if len(splitOutput) > 1 { + pkg.Version = splitOutput[1] + } +} diff --git a/v2/internal/system/packagemanager/dnf.go b/v2/internal/system/packagemanager/dnf.go new file mode 100644 index 000000000..b7a760f2c --- /dev/null +++ b/v2/internal/system/packagemanager/dnf.go @@ -0,0 +1,129 @@ +// +build linux + +package packagemanager + +import ( + "os/exec" + "strings" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +// Dnf represents the Dnf manager +type Dnf struct { + name string + osid string +} + +// NewDnf creates a new Dnf instance +func NewDnf(osid string) *Dnf { + return &Dnf{ + name: "dnf", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (y *Dnf) Packages() packagemap { + return packagemap{ + "libgtk-3": []*Package{ + {Name: "gtk3-devel", SystemPackage: true, Library: true}, + }, + "libwebkit": []*Package{ + {Name: "webkit2gtk3-devel", SystemPackage: true, Library: true}, + // {Name: "webkitgtk3-devel", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc-c++", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkgconf-pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm", SystemPackage: true}, + }, + "upx": []*Package{ + {Name: "upx", SystemPackage: true, Optional: true}, + }, + "docker": []*Package{ + { + SystemPackage: false, + Optional: true, + InstallCommand: map[string]string{ + "centos": "Follow the guide: https://docs.docker.com/engine/install/centos/", + "fedora": "Follow the guide: https://docs.docker.com/engine/install/fedora/", + }, + }, + }, + } +} + +// Name returns the name of the package manager +func (y *Dnf) Name() string { + return y.name +} + +// PackageInstalled tests if the given package name is installed +func (y *Dnf) PackageInstalled(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "dnf", "info", "installed", pkg.Name) + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + splitoutput := strings.Split(stdout, "\n") + for _, line := range splitoutput { + if strings.HasPrefix(line, "Version") { + splitline := strings.Split(line, ":") + pkg.Version = strings.TrimSpace(splitline[1]) + } + } + + return true, err +} + +// PackageAvailable tests if the given package is available for installation +func (y *Dnf) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "dnf", "info", pkg.Name) + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + splitoutput := strings.Split(stdout, "\n") + for _, line := range splitoutput { + if strings.HasPrefix(line, "Version") { + splitline := strings.Split(line, ":") + pkg.Version = strings.TrimSpace(splitline[1]) + } + } + return true, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (y *Dnf) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand[y.osid] + } + return "sudo dnf install " + pkg.Name +} + +func (y *Dnf) getPackageVersion(pkg *Package, output string) { + splitOutput := strings.Split(output, " ") + if len(splitOutput) > 0 { + pkg.Version = splitOutput[1] + } +} diff --git a/v2/internal/system/packagemanager/emerge.go b/v2/internal/system/packagemanager/emerge.go new file mode 100644 index 000000000..0caa3117c --- /dev/null +++ b/v2/internal/system/packagemanager/emerge.go @@ -0,0 +1,117 @@ +// +build linux + +package packagemanager + +import ( + "os/exec" + "regexp" + "strings" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +// Emerge represents the Emerge package manager +type Emerge struct { + name string + osid string +} + +// NewEmerge creates a new Emerge instance +func NewEmerge(osid string) *Emerge { + return &Emerge{ + name: "emerge", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (e *Emerge) Packages() packagemap { + return packagemap{ + "libgtk-3": []*Package{ + {Name: "x11-libs/gtk+", SystemPackage: true, Library: true}, + }, + "libwebkit": []*Package{ + {Name: "net-libs/webkit-gtk", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "sys-devel/gcc", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "dev-util/pkgconf", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "net-libs/nodejs", SystemPackage: true}, + }, + "docker": []*Package{ + {Name: "app-emulation/docker", SystemPackage: true, Optional: true}, + }, + } +} + +// Name returns the name of the package manager +func (e *Emerge) Name() string { + return e.name +} + +// PackageInstalled tests if the given package name is installed +func (e *Emerge) PackageInstalled(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "emerge", "-s", pkg.Name+"$") + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + regex := `.*\*\s+` + regexp.QuoteMeta(pkg.Name) + `\n(?:\S|\s)+?Latest version installed: (.*)` + installedRegex := regexp.MustCompile(regex) + matches := installedRegex.FindStringSubmatch(stdout) + pkg.Version = "" + noOfMatches := len(matches) + installed := false + if noOfMatches > 1 && matches[1] != "[ Not Installed ]" { + installed = true + pkg.Version = strings.TrimSpace(matches[1]) + } + return installed, err +} + +// PackageAvailable tests if the given package is available for installation +func (e *Emerge) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "emerge", "-s", pkg.Name+"$") + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + installedRegex := regexp.MustCompile(`.*\*\s+` + regexp.QuoteMeta(pkg.Name) + `\n(?:\S|\s)+?Latest version available: (.*)`) + matches := installedRegex.FindStringSubmatch(stdout) + pkg.Version = "" + noOfMatches := len(matches) + available := false + if noOfMatches > 1 { + available = true + pkg.Version = strings.TrimSpace(matches[1]) + } + return available, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (e *Emerge) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand[e.osid] + } + return "sudo emerge " + pkg.Name +} diff --git a/v2/internal/system/packagemanager/eopkg.go b/v2/internal/system/packagemanager/eopkg.go new file mode 100644 index 000000000..6a7a8fd95 --- /dev/null +++ b/v2/internal/system/packagemanager/eopkg.go @@ -0,0 +1,114 @@ +// +build linux + +package packagemanager + +import ( + "regexp" + "strings" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +// Eopkg represents the Eopkg manager +type Eopkg struct { + name string + osid string +} + +// NewEopkg creates a new Eopkg instance +func NewEopkg(osid string) *Eopkg { + result := &Eopkg{ + name: "eopkg", + osid: osid, + } + result.intialiseName() + return result +} + +// Packages returns the packages that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (e *Eopkg) Packages() packagemap { + return packagemap{ + "libgtk-3": []*Package{ + {Name: "libgtk-3-devel", SystemPackage: true, Library: true}, + }, + "libwebkit": []*Package{ + {Name: "libwebkit-gtk-devel", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "nodejs", SystemPackage: true}, + }, + "docker": []*Package{ + {Name: "docker", SystemPackage: true, Optional: true}, + }, + } +} + +// Name returns the name of the package manager +func (e *Eopkg) Name() string { + return e.name +} + +// PackageInstalled tests if the given package is installed +func (e *Eopkg) PackageInstalled(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "eopkg", "info", pkg.Name) + return strings.HasPrefix(stdout, "Installed"), err +} + +// PackageAvailable tests if the given package is available for installation +func (e *Eopkg) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "eopkg", "info", pkg.Name) + // We add a space to ensure we get a full match, not partial match + output := e.removeEscapeSequences(stdout) + installed := strings.Contains(output, "Package found in Solus repository") + e.getPackageVersion(pkg, output) + return installed, err +} + +// InstallCommand returns the package manager specific command to install a package +func (e *Eopkg) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand[e.osid] + } + return "sudo eopkg it " + pkg.Name +} + +func (e *Eopkg) removeEscapeSequences(in string) string { + escapechars, _ := regexp.Compile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])`) + return escapechars.ReplaceAllString(in, "") +} + +func (e *Eopkg) intialiseName() { + result := "eopkg" + stdout, _, err := shell.RunCommand(".", "eopkg", "--version") + if err == nil { + result = strings.TrimSpace(stdout) + } + e.name = result +} + +func (e *Eopkg) getPackageVersion(pkg *Package, output string) { + + versionRegex := regexp.MustCompile(`.*Name.*version:\s+(.*)+, release: (.*)`) + matches := versionRegex.FindStringSubmatch(output) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + pkg.Version = matches[1] + if noOfMatches > 2 { + pkg.Version += " (r" + matches[2] + ")" + } + } +} diff --git a/v2/internal/system/packagemanager/packagemanager.go b/v2/internal/system/packagemanager/packagemanager.go new file mode 100644 index 000000000..7e15594aa --- /dev/null +++ b/v2/internal/system/packagemanager/packagemanager.go @@ -0,0 +1,158 @@ +// +build linux + +package packagemanager + +import ( + "sort" + "strings" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +// A list of package manager commands +var pmcommands = []string{ + "eopkg", + "apt", + "dnf", + "pacman", + "emerge", + "zypper", +} + +// Find will attempt to find the system package manager +func Find(osid string) PackageManager { + + // Loop over pmcommands + for _, pmname := range pmcommands { + if shell.CommandExists(pmname) { + return newPackageManager(pmname, osid) + } + } + return nil +} + +func newPackageManager(pmname string, osid string) PackageManager { + switch pmname { + case "eopkg": + return NewEopkg(osid) + case "apt": + return NewApt(osid) + case "dnf": + return NewDnf(osid) + case "pacman": + return NewPacman(osid) + case "emerge": + return NewEmerge(osid) + case "zypper": + return NewZypper(osid) + } + return nil +} + +// Dependancies scans the system for required dependancies +// Returns a list of dependancies search for, whether they were found +// and whether they were installed +func Dependancies(p PackageManager) (DependencyList, error) { + + var dependencies DependencyList + + for name, packages := range p.Packages() { + dependency := &Dependancy{Name: name} + for _, pkg := range packages { + dependency.Optional = pkg.Optional + dependency.External = !pkg.SystemPackage + dependency.InstallCommand = p.InstallCommand(pkg) + packageavailable, err := p.PackageAvailable(pkg) + if err != nil { + return nil, err + } + if packageavailable { + dependency.Version = pkg.Version + dependency.PackageName = pkg.Name + installed, err := p.PackageInstalled(pkg) + if err != nil { + return nil, err + } + if installed { + dependency.Installed = true + dependency.Version = pkg.Version + if !pkg.SystemPackage { + dependency.Version = AppVersion(name) + } + } else { + dependency.InstallCommand = p.InstallCommand(pkg) + } + break + } + } + dependencies = append(dependencies, dependency) + } + + // Sort dependencies + sort.Slice(dependencies, func(i, j int) bool { + return dependencies[i].Name < dependencies[j].Name + }) + + return dependencies, nil +} + +// AppVersion returns the version for application related to the given package +func AppVersion(name string) string { + + if name == "gcc" { + return gccVersion() + } + + if name == "pkg-config" { + return pkgConfigVersion() + } + + if name == "npm" { + return npmVersion() + } + + if name == "docker" { + return dockerVersion() + } + + return "" + +} + +func gccVersion() string { + + var version string + var err error + + // Try "-dumpfullversion" + version, _, err = shell.RunCommand(".", "gcc", "-dumpfullversion") + if err != nil { + + // Try -dumpversion + // We ignore the error as this function is not for testing whether the + // application exists, only that we can get the version number + dumpversion, _, err := shell.RunCommand(".", "gcc", "-dumpversion") + if err == nil { + version = dumpversion + } + } + return strings.TrimSpace(version) +} + +func pkgConfigVersion() string { + version, _, _ := shell.RunCommand(".", "pkg-config", "--version") + return strings.TrimSpace(version) +} + +func npmVersion() string { + version, _, _ := shell.RunCommand(".", "npm", "--version") + return strings.TrimSpace(version) +} + +func dockerVersion() string { + version, _, _ := shell.RunCommand(".", "docker", "--version") + version = strings.TrimPrefix(version, "Docker version ") + version = strings.ReplaceAll(version, ", build ", " (") + version = strings.TrimSpace(version) + ")" + return version +} diff --git a/v2/internal/system/packagemanager/pacman.go b/v2/internal/system/packagemanager/pacman.go new file mode 100644 index 000000000..6378c324d --- /dev/null +++ b/v2/internal/system/packagemanager/pacman.go @@ -0,0 +1,114 @@ +// +build linux + +package packagemanager + +import ( + "os/exec" + "regexp" + "strings" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +// Pacman represents the Pacman package manager +type Pacman struct { + name string + osid string +} + +// NewPacman creates a new Pacman instance +func NewPacman(osid string) *Pacman { + return &Pacman{ + name: "pacman", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (p *Pacman) Packages() packagemap { + return packagemap{ + "libgtk-3": []*Package{ + {Name: "gtk3", SystemPackage: true, Library: true}, + }, + "libwebkit": []*Package{ + {Name: "webkit2gtk", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkgconf", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm", SystemPackage: true}, + }, + "docker": []*Package{ + {Name: "docker", SystemPackage: true, Optional: true}, + }, + } +} + +// Name returns the name of the package manager +func (p *Pacman) Name() string { + return p.name +} + +// PackageInstalled tests if the given package name is installed +func (p *Pacman) PackageInstalled(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "pacman", "-Q", pkg.Name) + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + splitoutput := strings.Split(stdout, "\n") + for _, line := range splitoutput { + if strings.HasPrefix(line, pkg.Name) { + splitline := strings.Split(line, " ") + pkg.Version = strings.TrimSpace(splitline[1]) + } + } + + return true, err +} + +// PackageAvailable tests if the given package is available for installation +func (p *Pacman) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + output, _, err := shell.RunCommand(".", "pacman", "-Si", pkg.Name) + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + reg := regexp.MustCompile(`.*Version.*?:\s+(.*)`) + matches := reg.FindStringSubmatch(output) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + pkg.Version = strings.TrimSpace(matches[1]) + } + + return true, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (p *Pacman) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand[p.osid] + } + return "sudo pacman -S " + pkg.Name +} diff --git a/v2/internal/system/packagemanager/pm.go b/v2/internal/system/packagemanager/pm.go new file mode 100644 index 000000000..c66d0d364 --- /dev/null +++ b/v2/internal/system/packagemanager/pm.go @@ -0,0 +1,62 @@ +package packagemanager + +// Package contains information about a system package +type Package struct { + Name string + Version string + InstallCommand map[string]string + SystemPackage bool + Library bool + Optional bool +} + +type packagemap = map[string][]*Package + +// PackageManager is a common interface across all package managers +type PackageManager interface { + Name() string + Packages() packagemap + PackageInstalled(*Package) (bool, error) + PackageAvailable(*Package) (bool, error) + InstallCommand(*Package) string +} + +// Dependancy represents a system package that we require +type Dependancy struct { + Name string + PackageName string + Installed bool + InstallCommand string + Version string + Optional bool + External bool +} + +// DependencyList is a list of Dependency instances +type DependencyList []*Dependancy + +// InstallAllRequiredCommand returns the command you need to use to install all required dependencies +func (d DependencyList) InstallAllRequiredCommand() string { + + result := "" + for _, dependency := range d { + if !dependency.Installed && !dependency.Optional { + result += " - " + dependency.Name + ": " + dependency.InstallCommand + "\n" + } + } + + return result +} + +// InstallAllOptionalCommand returns the command you need to use to install all optional dependencies +func (d DependencyList) InstallAllOptionalCommand() string { + + result := "" + for _, dependency := range d { + if !dependency.Installed && dependency.Optional { + result += " - " + dependency.Name + ": " + dependency.InstallCommand + "\n" + } + } + + return result +} diff --git a/v2/internal/system/packagemanager/zypper.go b/v2/internal/system/packagemanager/zypper.go new file mode 100644 index 000000000..af9d2e3d1 --- /dev/null +++ b/v2/internal/system/packagemanager/zypper.go @@ -0,0 +1,120 @@ +// +build linux + +package packagemanager + +import ( + "os/exec" + "regexp" + "strings" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +// Zypper represents the Zypper package manager +type Zypper struct { + name string + osid string +} + +// NewZypper creates a new Zypper instance +func NewZypper(osid string) *Zypper { + return &Zypper{ + name: "zypper", + osid: osid, + } +} + +// Packages returns the libraries that we need for Wails to compile +// They will potentially differ on different distributions or versions +func (z *Zypper) Packages() packagemap { + return packagemap{ + "libgtk-3": []*Package{ + {Name: "gtk3-devel", SystemPackage: true, Library: true}, + }, + "libwebkit": []*Package{ + {Name: "webkit2gtk3-devel", SystemPackage: true, Library: true}, + }, + "gcc": []*Package{ + {Name: "gcc-c++", SystemPackage: true}, + }, + "pkg-config": []*Package{ + {Name: "pkg-config", SystemPackage: true}, + }, + "npm": []*Package{ + {Name: "npm10", SystemPackage: true}, + }, + "docker": []*Package{ + {Name: "docker", SystemPackage: true, Optional: true}, + }, + } +} + +// Name returns the name of the package manager +func (z *Zypper) Name() string { + return z.name +} + +// PackageInstalled tests if the given package name is installed +func (z *Zypper) PackageInstalled(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "zypper", "info", pkg.Name) + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + reg := regexp.MustCompile(`.*Installed\s*:\s*(Yes)\s*`) + matches := reg.FindStringSubmatch(stdout) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + z.getPackageVersion(pkg, stdout) + } + return noOfMatches > 1, err +} + +// PackageAvailable tests if the given package is available for installation +func (z *Zypper) PackageAvailable(pkg *Package) (bool, error) { + if pkg.SystemPackage == false { + return false, nil + } + stdout, _, err := shell.RunCommand(".", "zypper", "info", pkg.Name) + // We add a space to ensure we get a full match, not partial match + if err != nil { + _, ok := err.(*exec.ExitError) + if ok { + return false, nil + } + return false, err + } + + available := strings.Contains(stdout, "Information for package") + if available { + z.getPackageVersion(pkg, stdout) + } + + return available, nil +} + +// InstallCommand returns the package manager specific command to install a package +func (z *Zypper) InstallCommand(pkg *Package) string { + if pkg.SystemPackage == false { + return pkg.InstallCommand[z.osid] + } + return "sudo zypper in " + pkg.Name +} + +func (z *Zypper) getPackageVersion(pkg *Package, output string) { + + reg := regexp.MustCompile(`.*Version.*:(.*)`) + matches := reg.FindStringSubmatch(output) + pkg.Version = "" + noOfMatches := len(matches) + if noOfMatches > 1 { + pkg.Version = strings.TrimSpace(matches[1]) + } +} diff --git a/v2/internal/system/system.go b/v2/internal/system/system.go new file mode 100644 index 000000000..9d7650622 --- /dev/null +++ b/v2/internal/system/system.go @@ -0,0 +1,111 @@ +package system + +import ( + "github.com/wailsapp/wails/v2/internal/system/operatingsystem" + "github.com/wailsapp/wails/v2/internal/system/packagemanager" + "os/exec" + "strings" +) + +var ( + IsAppleSilicon bool +) + +// Info holds information about the current operating system, +// package manager and required dependancies +type Info struct { + OS *operatingsystem.OS + PM packagemanager.PackageManager + Dependencies packagemanager.DependencyList +} + +// GetInfo scans the system for operating system details, +// the system package manager and the status of required +// dependancies. +func GetInfo() (*Info, error) { + var result Info + err := result.discover() + if err != nil { + return nil, err + } + return &result, nil +} + +func checkNPM() *packagemanager.Dependancy { + + // Check for npm + output, err := exec.Command("npm", "-version").Output() + installed := true + version := "" + if err != nil { + installed = false + } else { + version = strings.TrimSpace(strings.Split(string(output), "\n")[0]) + } + return &packagemanager.Dependancy{ + Name: "npm ", + PackageName: "N/A", + Installed: installed, + InstallCommand: "Available at https://nodejs.org/en/download/", + Version: version, + Optional: false, + External: false, + } +} + +func checkUPX() *packagemanager.Dependancy { + + // Check for npm + output, err := exec.Command("upx", "-V").Output() + installed := true + version := "" + if err != nil { + installed = false + } else { + version = strings.TrimSpace(strings.Split(string(output), "\n")[0]) + } + return &packagemanager.Dependancy{ + Name: "upx ", + PackageName: "N/A", + Installed: installed, + InstallCommand: "Available at https://upx.github.io/", + Version: version, + Optional: true, + External: false, + } +} + +func checkDocker() *packagemanager.Dependancy { + + // Check for npm + output, err := exec.Command("docker", "version").Output() + installed := true + version := "" + + // Docker errors if it is not running so check for that + if len(output) == 0 && err != nil { + installed = false + } else { + // Version is in a line like: " Version: 20.10.5" + versionOutput := strings.Split(string(output), "\n") + for _, line := range versionOutput[1:] { + splitLine := strings.Split(line, ":") + if len(splitLine) > 1 { + key := strings.TrimSpace(splitLine[0]) + if key == "Version" { + version = strings.TrimSpace(splitLine[1]) + break + } + } + } + } + return &packagemanager.Dependancy{ + Name: "docker ", + PackageName: "N/A", + Installed: installed, + InstallCommand: "Available at https://www.docker.com/products/docker-desktop", + Version: version, + Optional: true, + External: false, + } +} diff --git a/v2/internal/system/system_darwin.go b/v2/internal/system/system_darwin.go new file mode 100644 index 000000000..671c6bd76 --- /dev/null +++ b/v2/internal/system/system_darwin.go @@ -0,0 +1,58 @@ +//go:build darwin +// +build darwin + +package system + +import ( + "github.com/wailsapp/wails/v2/internal/system/packagemanager" + "os/exec" + "strings" + "syscall" + + "github.com/wailsapp/wails/v2/internal/system/operatingsystem" +) + +// Determine if the app is running on Apple Silicon +// Credit: https://www.yellowduck.be/posts/detecting-apple-silicon-via-go/ +func init() { + r, err := syscall.Sysctl("sysctl.proc_translated") + if err != nil { + return + } + + IsAppleSilicon = r == "\x00\x00\x00" || r == "\x01\x00\x00" +} + +func (i *Info) discover() error { + var err error + osinfo, err := operatingsystem.Info() + if err != nil { + return err + } + i.OS = osinfo + + // Check for xcode command line tools + output, err := exec.Command("xcode-select", "-v").Output() + installed := true + version := "" + if err != nil { + installed = false + } else { + version = strings.TrimPrefix(string(output), "xcode-select version ") + version = strings.TrimSpace(version) + version = strings.TrimSuffix(version, ".") + } + xcodeDep := &packagemanager.Dependancy{ + Name: "xcode command line tools ", + PackageName: "N/A", + Installed: installed, + InstallCommand: "xcode-select --install", + Version: version, + Optional: false, + External: false, + } + i.Dependencies = append(i.Dependencies, xcodeDep) + i.Dependencies = append(i.Dependencies, checkNPM()) + i.Dependencies = append(i.Dependencies, checkUPX()) + return nil +} diff --git a/v2/internal/system/system_linux.go b/v2/internal/system/system_linux.go new file mode 100644 index 000000000..5a08d96b7 --- /dev/null +++ b/v2/internal/system/system_linux.go @@ -0,0 +1,30 @@ +//go:build linux +// +build linux + +package system + +import ( + "github.com/wailsapp/wails/v2/internal/system/operatingsystem" + "github.com/wailsapp/wails/v2/internal/system/packagemanager" +) + +func (i *Info) discover() error { + + var err error + osinfo, err := operatingsystem.Info() + if err != nil { + return err + } + i.OS = osinfo + + i.PM = packagemanager.Find(osinfo.ID) + if i.PM != nil { + dependencies, err := packagemanager.Dependancies(i.PM) + if err != nil { + return err + } + i.Dependencies = dependencies + } + + return nil +} diff --git a/v2/internal/system/system_windows.go b/v2/internal/system/system_windows.go new file mode 100644 index 000000000..94c3b23a2 --- /dev/null +++ b/v2/internal/system/system_windows.go @@ -0,0 +1,48 @@ +//go:build windows +// +build windows + +package system + +import ( + "github.com/leaanthony/webview2runtime" + "github.com/wailsapp/wails/v2/internal/system/operatingsystem" + "github.com/wailsapp/wails/v2/internal/system/packagemanager" +) + +func (i *Info) discover() error { + + var err error + osinfo, err := operatingsystem.Info() + if err != nil { + return err + } + i.OS = osinfo + + i.Dependencies = append(i.Dependencies, checkWebView2()) + i.Dependencies = append(i.Dependencies, checkNPM()) + i.Dependencies = append(i.Dependencies, checkUPX()) + //i.Dependencies = append(i.Dependencies, checkDocker()) + + return nil +} + +func checkWebView2() *packagemanager.Dependancy { + + info := webview2runtime.GetInstalledVersion() + version := "" + if info != nil { + version = info.Version + } + installed := version != "" + + return &packagemanager.Dependancy{ + Name: "WebView2 ", + PackageName: "N/A", + Installed: installed, + InstallCommand: "Available at https://developer.microsoft.com/en-us/microsoft-edge/webview2/", + Version: version, + Optional: false, + External: true, + } + +} diff --git a/v2/internal/webserver/routes.go b/v2/internal/webserver/routes.go new file mode 100644 index 000000000..3b65b2e79 --- /dev/null +++ b/v2/internal/webserver/routes.go @@ -0,0 +1,49 @@ +package webserver + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" +) + +func (w *WebServer) setUpRoutes() error { + // Handle Index + assets + http.HandleFunc("/", w.serveAssets) + + // Handle Bindings + http.HandleFunc("/bindings.js", w.serveBindings) + + // Handle Wails + http.HandleFunc("/wails.js", w.serveWails) + + // Handle websocket connection + http.HandleFunc("/ws", w.websocketConnection) + + return nil +} + +func (w *WebServer) serveAssets(resp http.ResponseWriter, req *http.Request) { + fileserver := http.FileServer(w.assets) + fileserver.ServeHTTP(resp, req) +} + +func (w *WebServer) serveBindings(resp http.ResponseWriter, req *http.Request) { + resp.Header().Add("content-type", "application/javascript") + bindings, err := w.bindings.ToJSON() + if err != nil { + w.logger.Error("Failed to convert bindings to JSON: %v", err) + fmt.Fprintf(resp, "") + return + } + w.logger.Debug("Sending bindings to webclient: %v", bindings) + b, _ := json.Marshal(bindings) + fmt.Fprintf(resp, fmt.Sprintf("window.wailsbindings=%s; window.SetBindings(window.wailsbindings);", b)) +} + +func (w *WebServer) serveWails(resp http.ResponseWriter, req *http.Request) { + resp.Header().Add("content-type", "application/javascript") + if data, err := w.assets.String("/wails.js"); err == nil { + fmt.Fprintf(resp, strings.Replace(data, ":8080", fmt.Sprintf(":%d", w.port), 1)) + } +} diff --git a/v2/internal/webserver/webassets.go b/v2/internal/webserver/webassets.go new file mode 100644 index 000000000..9f73c8f77 --- /dev/null +++ b/v2/internal/webserver/webassets.go @@ -0,0 +1,9 @@ +package webserver + +import "github.com/wailsapp/wails/v2/internal/assetdb" + +var ( + // WebAssets is our single asset db instance. + // It will be constructed by a dynamically generated method in this directory. + WebAssets *assetdb.AssetDB = assetdb.NewAssetDB() +) diff --git a/v2/internal/webserver/webserver.go b/v2/internal/webserver/webserver.go new file mode 100644 index 000000000..4b143d250 --- /dev/null +++ b/v2/internal/webserver/webserver.go @@ -0,0 +1,87 @@ +package webserver + +import ( + "fmt" + "net/http" + "sync" + + "github.com/wailsapp/wails/v2/internal/assetdb" + "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/internal/messagedispatcher" + "github.com/wailsapp/wails/v2/internal/subsystem" +) + +// WebServer serves the application over http +type WebServer struct { + port int + ip string + assets *assetdb.AssetDB + logger *logger.Logger + dispatcher *messagedispatcher.Dispatcher + event *subsystem.Event + server *http.Server + bindings *binding.Bindings + + lock sync.Mutex + connections map[string]*WebClient +} + +// NewWebServer creates a new WebServer +func NewWebServer(logger *logger.Logger) *WebServer { + + // Return a WebServer with default values + return &WebServer{ + assets: db, + connections: make(map[string]*WebClient), + logger: logger, + } +} + +// URL returns the URL that the server is serving from +func (w *WebServer) URL() string { + return fmt.Sprintf("http://%s:%d", w.ip, w.port) +} + +// SetPort sets the server port to listen on +func (w *WebServer) SetPort(port int) { + w.port = port +} + +// SetIP sets the server ip to listen on +func (w *WebServer) SetIP(ip string) { + w.ip = ip +} + +// SetBindings provides the webserver with the mapping of bindings to provide to a client +func (w *WebServer) SetBindings(bindings *binding.Bindings) { + w.bindings = bindings +} + +// Start the webserver +func (w *WebServer) Start(dispatcher *messagedispatcher.Dispatcher, event *subsystem.Event) error { + var err error + + // Create the server + w.server = &http.Server{Addr: fmt.Sprintf("%s:%d", w.ip, w.port)} + w.event = event + + // Set up the Web Server's routes + err = w.setUpRoutes() + if err != nil { + return err + } + + // Save the dispatcher + w.dispatcher = dispatcher + + // Start the WebServer + err = w.server.ListenAndServe() + + // Return any error except http.ErrServerClosed + if err != nil && err != http.ErrServerClosed { + return err + } + + return nil +} diff --git a/v2/internal/webserver/websockets.go b/v2/internal/webserver/websockets.go new file mode 100644 index 000000000..474ccfded --- /dev/null +++ b/v2/internal/webserver/websockets.go @@ -0,0 +1,238 @@ +package webserver + +import ( + "context" + "github.com/wailsapp/wails/v2/pkg/menu" + "github.com/wailsapp/wails/v2/pkg/runtime" + "net/http" + "strings" + + "github.com/wailsapp/wails/v2/internal/logger" + ws "nhooyr.io/websocket" + "nhooyr.io/websocket/wsjson" +) + +// WebClient represents an individual web session +type WebClient struct { + conn *ws.Conn + identifier string + logger *logger.Logger + running bool +} + +func (wc *WebClient) WindowSetMinSize(width int, height int) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowSetMaxSize(width int, height int) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) DeleteTrayMenuByID(id string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) SetTrayMenu(trayMenuJSON string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) UpdateTrayMenuLabel(trayMenuJSON string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) MessageDialog(dialogOptions runtime.MessageDialogOptions, callbackID string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) SetApplicationMenu(menuJSON string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) UpdateTrayMenu(trayMenuJSON string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) UpdateContextMenu(contextMenuJSON string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) OpenFileDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) OpenMultipleFilesDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) OpenDirectoryDialog(dialogOptions runtime.OpenDialogOptions, callbackID string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) SaveDialog(dialogOptions runtime.SaveDialogOptions, callbackID string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowShow() { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowHide() { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowCenter() { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowMaximise() { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowUnmaximise() { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowMinimise() { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowUnminimise() { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowPosition(x int, y int) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) WindowSize(width int, height int) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) DarkModeEnabled(callbackID string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) UpdateMenu(menu *menu.Menu) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) UpdateTray(menu *menu.Menu) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) UpdateTrayLabel(label string) { + wc.logger.Info("Not implemented in server build") +} + +func (wc *WebClient) UpdateTrayIcon(name string) { + wc.logger.Info("Not implemented in server build") +} + +// Quit terminates the webclient session +func (wc *WebClient) Quit() { + wc.running = false +} + +// NotifyEvent sends the event +func (wc *WebClient) NotifyEvent(message string) { + wc.SendMessage("E" + message) +} + +// CallResult sends the result of the Go function call back to the +// originator in the frontend +func (wc *WebClient) CallResult(message string) { + wc.SendMessage("R" + message) +} + +// WindowSetTitle is a noop in the webclient +func (wc *WebClient) WindowSetTitle(title string) {} + +// WindowFullscreen is a noop in the webclient +func (wc *WebClient) WindowFullscreen() {} + +// WindowUnFullscreen is a noop in the webclient +func (wc *WebClient) WindowUnFullscreen() {} + +// WindowSetColour is a noop in the webclient +func (wc *WebClient) WindowSetColour(colour int) { +} + +// Run processes messages from the remote webclient +func (wc *WebClient) Run(w *WebServer) { + dispatcher := w.dispatcher.RegisterClient(wc) + defer w.dispatcher.RemoveClient(dispatcher) + defer w.unregisterClient(wc.identifier) + + for wc.running { + var v interface{} + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + if err := wsjson.Read(ctx, wc.conn, &v); err != nil { + if ws.CloseStatus(err) == ws.StatusNormalClosure || ws.CloseStatus(err) == ws.StatusGoingAway { + break + } + if ws.CloseStatus(err) != -1 { + w.logger.Debug("Connection error: %s - %s", wc.identifier, err) + break + } + + if !strings.Contains(err.Error(), "status = Status") { + w.logger.Debug("Error encountered on socket: %v", err) + break + } + } + dispatcher.DispatchMessage(v.(string)) + } + + err := wc.conn.Close(ws.StatusNormalClosure, "Goodbye") + if err != nil { + w.logger.Error("Error encountered on socket: %v", err) + return + } + w.logger.Debug("Connection closed: %v", wc.identifier) +} + +// SendMessage converts the string to a []byte and passes it to +// the connection's Writer to send to the remote client +// The Writer itself prevents multiple users at the same time. +func (wc *WebClient) SendMessage(message string) { + wc.logger.Debug("WebClient.SendMessage() - %s", message) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + err := wc.conn.Write(ctx, ws.MessageText, []byte(message)) + if err != nil { + wc.logger.Error("Error encountered writing to webclient: %v", err) + } +} + +// unregisterClient is called automatically by a WebClient session during termination +// so that it's registration can be removed +func (w *WebServer) unregisterClient(identifier string) { + w.logger.Debug("Removing WebClient : %v", identifier) + w.lock.Lock() + delete(w.connections, identifier) + w.lock.Unlock() +} + +func (w *WebServer) websocketConnection(resp http.ResponseWriter, req *http.Request) { + conn, err := ws.Accept(resp, req, nil) + if err != nil { + w.logger.Debug("Failed to upgrade websocket connection") + return + } + wc := &WebClient{ + conn: conn, + identifier: req.RemoteAddr, + logger: w.logger, + running: true, + } + w.lock.Lock() + w.connections[wc.identifier] = wc + w.lock.Unlock() + + w.logger.Debug("Connection from: %v", wc.identifier) + + go wc.Run(w) + +} diff --git a/v2/pkg/buildassets/build/README.md b/v2/pkg/buildassets/build/README.md new file mode 100644 index 000000000..c01ea3cb1 --- /dev/null +++ b/v2/pkg/buildassets/build/README.md @@ -0,0 +1,61 @@ +# Build Directory + +The build directory is used to house all the build files and assets for your application. + +The structure is: + + * bin - Output directory + * dialog - Icons for dialogs + * tray - Icons for the system tray + * mac - MacOS specific files + * linux - Linux specific files + * windows - Windows specific files + +## Dialog Icons + +Place any PNG file in this directory to be able to use them in message dialogs. +The files should have names in the following format: `name[-(light|dark)][2x].png` + +Examples: + +* `mypic.png` - Standard definition icon with ID `mypic` +* `mypic-light.png` - Standard definition icon with ID `mypic`, used when system theme is light +* `mypic-dark.png` - Standard definition icon with ID `mypic`, used when system theme is dark +* `mypic2x.png` - High definition icon with ID `mypic` +* `mypic-light2x.png` - High definition icon with ID `mypic`, used when system theme is light +* `mypic-dark2x.png` - High definition icon with ID `mypic`, used when system theme is dark + +### Order of preference + +Icons are selected with the following order of preference: + +For High Definition displays: +* name-(theme)2x.png +* name2x.png +* name-(theme).png +* name.png + +For Standard Definition displays: +* name-(theme).png +* name.png + +## Tray + +Place any PNG file in this directory to be able to use them as tray icons. +The name of the filename will be the ID to reference the image. + +Example: + +* `mypic.png` - May be referenced using `runtime.Tray.SetIcon("mypic")` + +## Mac + +The `darwin` directory holds files specific to Mac builds, such as `Info.plist`. +These may be customised and used as part of the build. To return these files to the default state, simply delete them and +build with the `-package` flag. + +## Windows + +The `windows` directory contains the manifest and rc files used when building with the `-package` flag. +These may be customised for your application. To return these files to the default state, simply delete them and +build with the `-package` flag. \ No newline at end of file diff --git a/v2/pkg/buildassets/build/appicon.png b/v2/pkg/buildassets/build/appicon.png new file mode 100644 index 000000000..1c1dba67c Binary files /dev/null and b/v2/pkg/buildassets/build/appicon.png differ diff --git a/v2/pkg/buildassets/build/darwin/Info.tmpl.plist b/v2/pkg/buildassets/build/darwin/Info.tmpl.plist new file mode 100644 index 000000000..7180c58de --- /dev/null +++ b/v2/pkg/buildassets/build/darwin/Info.tmpl.plist @@ -0,0 +1,14 @@ + + + CFBundlePackageTypeAPPL + CFBundleName{{.Name}} + CFBundleExecutable{{.Name}} + CFBundleIdentifiercom.wails.{{.Name}} + CFBundleVersion1.0.0 + CFBundleGetInfoStringBuilt using Wails (https://wails.app) + CFBundleShortVersionString1.0.0 + CFBundleIconFileiconfile + LSMinimumSystemVersion10.13.0 + NSHighResolutionCapabletrue + NSHumanReadableCopyrightCopyright......... + \ No newline at end of file diff --git a/v2/pkg/buildassets/build/windows/icon.ico b/v2/pkg/buildassets/build/windows/icon.ico new file mode 100644 index 000000000..f33479841 Binary files /dev/null and b/v2/pkg/buildassets/build/windows/icon.ico differ diff --git a/v2/pkg/buildassets/build/windows/wails.exe.manifest b/v2/pkg/buildassets/build/windows/wails.exe.manifest new file mode 100644 index 000000000..0cb94320a --- /dev/null +++ b/v2/pkg/buildassets/build/windows/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/v2/pkg/buildassets/buildassets.go b/v2/pkg/buildassets/buildassets.go new file mode 100644 index 000000000..138b387fd --- /dev/null +++ b/v2/pkg/buildassets/buildassets.go @@ -0,0 +1,52 @@ +package buildassets + +import ( + "embed" + "github.com/leaanthony/debme" + "github.com/leaanthony/gosod" + "os" + "path/filepath" +) + +//go:embed build +var assets embed.FS + +type assetData struct { + Name string +} + +// Install will install all default project assets +func Install(targetDir string, projectName string) error { + templateDir := gosod.New(assets) + err := templateDir.Extract(targetDir, &assetData{Name: projectName}) + if err != nil { + return err + } + + // Rename the manifest file + windowsDir := filepath.Join(targetDir, "build", "windows") + manifest := filepath.Join(windowsDir, "wails.exe.manifest") + targetFile := filepath.Join(windowsDir, projectName+".exe.manifest") + err = os.Rename(manifest, targetFile) + if err != nil { + return err + } + + return nil +} + +func RegenerateManifest(target string) error { + a, err := debme.FS(assets, "build") + if err != nil { + return err + } + return a.CopyFile("windows/wails.exe.manifest", target, 0644) +} + +func RegenerateAppIcon(target string) error { + a, err := debme.FS(assets, "build") + if err != nil { + return err + } + return a.CopyFile("appicon.png", target, 0644) +} diff --git a/v2/pkg/buildassets/onhold/dialog/README.md b/v2/pkg/buildassets/onhold/dialog/README.md new file mode 100644 index 000000000..3b9189a8f --- /dev/null +++ b/v2/pkg/buildassets/onhold/dialog/README.md @@ -0,0 +1,29 @@ +## Dialog + +NOTE: Currently, this is a Mac only feature. + +Place any PNG file in this directory to be able to use them in message dialogs. +The files should have names in the following format: `name[-(light|dark)][2x].png` + +Examples: + +* `mypic.png` - Standard definition icon with ID `mypic` +* `mypic-light.png` - Standard definition icon with ID `mypic`, used when system theme is light +* `mypic-dark.png` - Standard definition icon with ID `mypic`, used when system theme is dark +* `mypic2x.png` - High definition icon with ID `mypic` +* `mypic-light2x.png` - High definition icon with ID `mypic`, used when system theme is light +* `mypic-dark2x.png` - High definition icon with ID `mypic`, used when system theme is dark + +### Order of preference + +Icons are selected with the following order of preference: + +For High Definition displays: +* name-(theme)2x.png +* name2x.png +* name-(theme).png +* name.png + +For Standard Definition displays: +* name-(theme).png +* name.png \ No newline at end of file diff --git a/v2/pkg/buildassets/onhold/dialog/info-dark.png b/v2/pkg/buildassets/onhold/dialog/info-dark.png new file mode 100644 index 000000000..9ff6655ee Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/info-dark.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/info-dark2x.png b/v2/pkg/buildassets/onhold/dialog/info-dark2x.png new file mode 100644 index 000000000..fcdf8006a Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/info-dark2x.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/info-light.png b/v2/pkg/buildassets/onhold/dialog/info-light.png new file mode 100644 index 000000000..1fb32e8a9 Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/info-light.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/info-light2x.png b/v2/pkg/buildassets/onhold/dialog/info-light2x.png new file mode 100644 index 000000000..874b2d301 Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/info-light2x.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/question-dark.png b/v2/pkg/buildassets/onhold/dialog/question-dark.png new file mode 100644 index 000000000..c2387420e Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/question-dark.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/question-dark2x.png b/v2/pkg/buildassets/onhold/dialog/question-dark2x.png new file mode 100644 index 000000000..86ea1b037 Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/question-dark2x.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/question-light.png b/v2/pkg/buildassets/onhold/dialog/question-light.png new file mode 100644 index 000000000..0d3b6ba02 Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/question-light.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/question-light2x.png b/v2/pkg/buildassets/onhold/dialog/question-light2x.png new file mode 100644 index 000000000..fcd21569f Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/question-light2x.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/warning-dark.png b/v2/pkg/buildassets/onhold/dialog/warning-dark.png new file mode 100644 index 000000000..db432321b Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/warning-dark.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/warning-dark2x.png b/v2/pkg/buildassets/onhold/dialog/warning-dark2x.png new file mode 100644 index 000000000..3325d22d2 Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/warning-dark2x.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/warning-light.png b/v2/pkg/buildassets/onhold/dialog/warning-light.png new file mode 100644 index 000000000..cf421a171 Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/warning-light.png differ diff --git a/v2/pkg/buildassets/onhold/dialog/warning-light2x.png b/v2/pkg/buildassets/onhold/dialog/warning-light2x.png new file mode 100644 index 000000000..ff092f6cd Binary files /dev/null and b/v2/pkg/buildassets/onhold/dialog/warning-light2x.png differ diff --git a/v2/pkg/buildassets/onhold/tray/README.md b/v2/pkg/buildassets/onhold/tray/README.md new file mode 100644 index 000000000..5f4e7b4e6 --- /dev/null +++ b/v2/pkg/buildassets/onhold/tray/README.md @@ -0,0 +1,8 @@ +## Tray + +Place any PNG file in this directory to be able to use them as tray icons. +The name of the filename will be the ID to reference the image. + +Example: + +* `mypic.png` - May be referenced using `runtime.Tray.SetIcon("mypic")` diff --git a/v2/pkg/clilogger/clilogger.go b/v2/pkg/clilogger/clilogger.go new file mode 100644 index 000000000..aa64fab5f --- /dev/null +++ b/v2/pkg/clilogger/clilogger.go @@ -0,0 +1,61 @@ +package clilogger + +import ( + "fmt" + "io" + "os" + + "github.com/wailsapp/wails/v2/internal/colour" +) + +// CLILogger is used by the cli +type CLILogger struct { + Writer io.Writer + mute bool +} + +// New cli logger +func New(writer io.Writer) *CLILogger { + return &CLILogger{ + Writer: writer, + } +} + +// Mute sets whether the logger should be muted +func (c *CLILogger) Mute(value bool) { + c.mute = value +} + +// Print works like Printf +func (c *CLILogger) Print(message string, args ...interface{}) { + if c.mute { + return + } + + _, err := fmt.Fprintf(c.Writer, message, args...) + if err != nil { + c.Fatal("Fatal: ", err) + } +} + +// Println works like Printf but with a line ending +func (c *CLILogger) Println(message string, args ...interface{}) { + if c.mute { + return + } + temp := fmt.Sprintf(message, args...) + _, err := fmt.Fprintln(c.Writer, temp) + if err != nil { + c.Fatal("Fatal: ", err) + } +} + +// Fatal prints the given message then aborts +func (c *CLILogger) Fatal(message string, args ...interface{}) { + temp := fmt.Sprintf(message, args...) + _, err := fmt.Fprintln(c.Writer, colour.Red("FATAL: "+temp)) + if err != nil { + println(colour.Red("FATAL: " + err.Error())) + } + os.Exit(1) +} diff --git a/v2/pkg/commands/build/base.go b/v2/pkg/commands/build/base.go new file mode 100644 index 000000000..022495a16 --- /dev/null +++ b/v2/pkg/commands/build/base.go @@ -0,0 +1,584 @@ +package build + +import ( + "bytes" + "fmt" + "github.com/leaanthony/gosod" + wailsRuntime "github.com/wailsapp/wails/v2/internal/frontend/runtime" + "github.com/wailsapp/wails/v2/internal/frontend/runtime/wrapper" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/pkg/errors" + + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/assetdb" + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/internal/html" + "github.com/wailsapp/wails/v2/internal/project" + "github.com/wailsapp/wails/v2/internal/shell" + "github.com/wailsapp/wails/v2/pkg/clilogger" +) + +const ( + VERBOSE int = 2 +) + +// BaseBuilder is the common builder struct +type BaseBuilder struct { + filesToDelete slicer.StringSlicer + projectData *project.Project + options *Options +} + +// NewBaseBuilder creates a new BaseBuilder +func NewBaseBuilder(options *Options) *BaseBuilder { + result := &BaseBuilder{ + options: options, + } + return result +} + +// SetProjectData sets the project data for this builder +func (b *BaseBuilder) SetProjectData(projectData *project.Project) { + b.projectData = projectData +} + +func (b *BaseBuilder) addFileToDelete(filename string) { + if !b.options.KeepAssets { + b.filesToDelete.Add(filename) + } +} + +func (b *BaseBuilder) fileExists(path string) bool { + // if file doesn't exist, ignore + _, err := os.Stat(path) + if err != nil { + return !os.IsNotExist(err) + } + return true +} + +// buildCustomAssets will iterate through the projects static directory and add all files +// to the application wide asset database. +func (b *BaseBuilder) buildCustomAssets(projectData *project.Project) error { + + // Add trailing slash to Asset directory + customAssetsDir := filepath.Join(projectData.Path, "assets", "custom") + "/" + if !b.fileExists(customAssetsDir) { + err := fs.MkDirs(customAssetsDir) + if err != nil { + return err + } + } + + assets := assetdb.NewAssetDB() + err := filepath.Walk(customAssetsDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + normalisedPath := filepath.ToSlash(path) + localPath := strings.TrimPrefix(normalisedPath, customAssetsDir) + if len(localPath) == 0 { + return nil + } + if data, err := ioutil.ReadFile(filepath.Join(customAssetsDir, localPath)); err == nil { + assets.AddAsset(localPath, data) + } + + return nil + }) + if err != nil { + return err + } + + // Write assetdb out to root directory + assetsDbFilename := fs.RelativePath("../../../assetsdb.go") + b.addFileToDelete(assetsDbFilename) + err = ioutil.WriteFile(assetsDbFilename, []byte(assets.Serialize("assets", "wails")), 0644) + if err != nil { + return err + } + return nil +} + +func (b *BaseBuilder) convertFileToIntegerString(filename string) (string, error) { + rawData, err := ioutil.ReadFile(filename) + if err != nil { + return "", err + } + return b.convertByteSliceToIntegerString(rawData), nil +} + +func (b *BaseBuilder) convertByteSliceToIntegerString(data []byte) string { + + // Create string builder + var result strings.Builder + + if len(data) > 0 { + + // Loop over all but 1 bytes + for i := 0; i < len(data)-1; i++ { + result.WriteString(fmt.Sprintf("%v,", data[i])) + } + + result.WriteString(fmt.Sprintf("%v", data[len(data)-1])) + + } + + return result.String() +} + +// CleanUp does post-build housekeeping +func (b *BaseBuilder) CleanUp() { + + // Delete all the files + b.filesToDelete.Each(func(filename string) { + + // if file doesn't exist, ignore + if !b.fileExists(filename) { + return + } + + // Delete file. We ignore errors because these files will be overwritten + // by the next build anyway. + _ = os.Remove(filename) + + }) +} + +func (b *BaseBuilder) OutputFilename(options *Options) string { + outputFile := options.OutputFile + if outputFile == "" { + target := strings.TrimSuffix(b.projectData.OutputFilename, ".exe") + if b.projectData.OutputType != "desktop" { + target += "-" + b.projectData.OutputType + } + // If we aren't using the standard compiler, add it to the filename + if options.Compiler != "go" { + // Parse the `go version` output. EG: `go version go1.16 windows/amd64` + stdout, _, err := shell.RunCommand(".", options.Compiler, "version") + if err != nil { + return "" + } + versionSplit := strings.Split(stdout, " ") + if len(versionSplit) == 4 { + target += "-" + versionSplit[2] + } + } + switch b.options.Platform { + case "windows": + outputFile = target + ".exe" + case "darwin", "linux": + if b.options.Arch == "" { + b.options.Arch = runtime.GOARCH + } + outputFile = fmt.Sprintf("%s-%s-%s", target, b.options.Platform, b.options.Arch) + } + + } + return outputFile +} + +// CompileProject compiles the project +func (b *BaseBuilder) CompileProject(options *Options) error { + + // Check if the runtime wrapper exists + err := generateRuntimeWrapper(options) + if err != nil { + return err + } + + verbose := options.Verbosity == VERBOSE + // Run go mod tidy first + cmd := exec.Command(options.Compiler, "mod", "tidy") + cmd.Stderr = os.Stderr + if verbose { + println("") + cmd.Stdout = os.Stdout + } + err = cmd.Run() + if err != nil { + return err + } + + // Default go build command + commands := slicer.String([]string{"build"}) + + // Add better debugging flags + if options.Mode == Dev { + commands.Add("-gcflags") + commands.Add(`"all=-N -l"`) + } + + //commands.Add("-a") + + var tags slicer.StringSlicer + tags.Add(options.OutputType) + tags.AddSlice(options.UserTags) + + // Add webview2 strategy if we have it + if options.WebView2Strategy != "" { + tags.Add(options.WebView2Strategy) + } + + if options.Mode == Production { + tags.Add("production") + } + + tags.Deduplicate() + + // Add the output type build tag + commands.Add("-tags") + commands.Add(tags.Join(",")) + + // LDFlags + ldflags := slicer.String() + if options.LDFlags != "" { + ldflags.Add(options.LDFlags) + } + + if options.Mode == Production { + ldflags.Add("-w", "-s") + if runtime.GOOS == "windows" { + ldflags.Add("-H windowsgui") + } + } + + ldflags.Deduplicate() + + if ldflags.Length() > 0 { + commands.Add("-ldflags") + commands.Add(ldflags.Join(" ")) + } + + // Get application build directory + appDir := options.BuildDirectory + if options.CleanBuildDirectory { + err = cleanBuildDirectory(options) + if err != nil { + return err + } + } + + // Set up output filename + outputFile := b.OutputFilename(options) + compiledBinary := filepath.Join(appDir, outputFile) + commands.Add("-o") + commands.Add(compiledBinary) + + b.projectData.OutputFilename = strings.TrimPrefix(compiledBinary, options.ProjectData.Path) + options.CompiledBinary = compiledBinary + + // Create the command + cmd = exec.Command(options.Compiler, commands.AsSlice()...) + cmd.Stderr = os.Stderr + if verbose { + println(" Build command:", commands.Join(" ")) + cmd.Stdout = os.Stdout + } + // Set the directory + cmd.Dir = b.projectData.Path + + // Add CGO flags + // We use the project/build dir as a temporary place for our generated c headers + buildBaseDir, err := fs.RelativeToCwd("build") + if err != nil { + return err + } + + cmd.Env = os.Environ() // inherit env + + if options.Platform != "windows" { + // Use upsertEnv so we don't overwrite user's CGO_CFLAGS + cmd.Env = upsertEnv(cmd.Env, "CGO_CFLAGS", func(v string) string { + if v != "" { + v += " " + } + v += "-I" + buildBaseDir + return v + }) + // Use upsertEnv so we don't overwrite user's CGO_CXXFLAGS + cmd.Env = upsertEnv(cmd.Env, "CGO_CXXFLAGS", func(v string) string { + if v != "" { + v += " " + } + v += "-I" + buildBaseDir + return v + }) + + cmd.Env = upsertEnv(cmd.Env, "CGO_ENABLED", func(v string) string { + return "1" + }) + } + + cmd.Env = upsertEnv(cmd.Env, "GOOS", func(v string) string { + return options.Platform + }) + + cmd.Env = upsertEnv(cmd.Env, "GOARCH", func(v string) string { + return options.Arch + }) + + if verbose { + println(" Environment:", strings.Join(cmd.Env, " ")) + } + + // Run command + err = cmd.Run() + cmd.Stderr = os.Stderr + + // Format error if we have one + if err != nil { + return err + } + + println("Done.") + + if !options.Compress { + return nil + } + + fmt.Printf("Compressing application: ") + + // Do we have upx installed? + if !shell.CommandExists("upx") { + println("Warning: Cannot compress binary: upx not found") + return nil + } + + var args = []string{"--best", "--no-color", "--no-progress", options.CompiledBinary} + + if options.CompressFlags != "" { + args = strings.Split(options.CompressFlags, " ") + args = append(args, options.CompiledBinary) + } + + if verbose { + println("upx", strings.Join(args, " ")) + } + + output, err := exec.Command("upx", args...).Output() + if err != nil { + return errors.Wrap(err, "Error during compression:") + } + println("Done.") + if verbose { + println(string(output)) + } + + return nil +} + +func generateRuntimeWrapper(options *Options) error { + wrapperDir := filepath.Join(options.WailsJSDir, "wailsjs", "runtime") + _ = os.RemoveAll(wrapperDir) + extractor := gosod.New(wrapper.RuntimeWrapper) + err := extractor.Extract(wrapperDir, nil) + if err != nil { + return err + } + + //ipcdev.js + err = os.WriteFile(filepath.Join(wrapperDir, "ipcdev.js"), wailsRuntime.DesktopIPC, 0755) + if err != nil { + return err + } + //runtimedev.js + err = os.WriteFile(filepath.Join(wrapperDir, "runtimedev.js"), wailsRuntime.RuntimeDesktopJS, 0755) + if err != nil { + return err + } + return nil +} + +// NpmInstall runs "npm install" in the given directory +func (b *BaseBuilder) NpmInstall(sourceDir string, verbose bool) error { + return b.NpmInstallUsingCommand(sourceDir, "npm install", verbose) +} + +// NpmInstallUsingCommand runs the given install command in the specified npm project directory +func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand string, verbose bool) error { + + packageJSON := filepath.Join(sourceDir, "package.json") + + // Check package.json exists + if !fs.FileExists(packageJSON) { + // No package.json, no install + return nil + } + + install := false + + // Get the MD5 sum of package.json + packageJSONMD5 := fs.MustMD5File(packageJSON) + + // Check whether we need to npm install + packageChecksumFile := filepath.Join(sourceDir, "package.json.md5") + if fs.FileExists(packageChecksumFile) { + // Compare checksums + storedChecksum := fs.MustLoadString(packageChecksumFile) + if storedChecksum != packageJSONMD5 { + fs.MustWriteString(packageChecksumFile, packageJSONMD5) + install = true + } + } else { + install = true + fs.MustWriteString(packageChecksumFile, packageJSONMD5) + } + + // Install if node_modules doesn't exist + nodeModulesDir := filepath.Join(sourceDir, "node_modules") + if !fs.DirExists(nodeModulesDir) { + install = true + } + + // check if forced install + if b.options.ForceBuild { + install = true + } + + // Shortcut installation + if install == false { + return nil + } + + // Split up the InstallCommand and execute it + cmd := strings.Split(installCommand, " ") + stdout, stderr, err := shell.RunCommand(sourceDir, cmd[0], cmd[1:]...) + if verbose || err != nil { + for _, l := range strings.Split(stdout, "\n") { + fmt.Printf(" %s\n", l) + } + for _, l := range strings.Split(stderr, "\n") { + fmt.Printf(" %s\n", l) + } + } + + return err +} + +// NpmRun executes the npm target in the provided directory +func (b *BaseBuilder) NpmRun(projectDir, buildTarget string, verbose bool) error { + stdout, stderr, err := shell.RunCommand(projectDir, "npm", "run", buildTarget) + if verbose || err != nil { + for _, l := range strings.Split(stdout, "\n") { + fmt.Printf(" %s\n", l) + } + for _, l := range strings.Split(stderr, "\n") { + fmt.Printf(" %s\n", l) + } + } + return err +} + +// NpmRunWithEnvironment executes the npm target in the provided directory, with the given environment variables +func (b *BaseBuilder) NpmRunWithEnvironment(projectDir, buildTarget string, verbose bool, envvars []string) error { + cmd := shell.CreateCommand(projectDir, "npm", "run", buildTarget) + cmd.Env = append(os.Environ(), envvars...) + var stdo, stde bytes.Buffer + cmd.Stdout = &stdo + cmd.Stderr = &stde + err := cmd.Run() + if verbose || err != nil { + for _, l := range strings.Split(stdo.String(), "\n") { + fmt.Printf(" %s\n", l) + } + for _, l := range strings.Split(stde.String(), "\n") { + fmt.Printf(" %s\n", l) + } + } + return err +} + +// BuildFrontend executes the `npm build` command for the frontend directory +func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error { + + verbose := b.options.Verbosity == VERBOSE + + frontendDir := filepath.Join(b.projectData.Path, "frontend") + + // Check there is an 'InstallCommand' provided in wails.json + if b.projectData.InstallCommand == "" { + // No - don't install + outputLogger.Println("No Install command. Skipping.") + } else { + // Do install if needed + outputLogger.Print("Installing frontend dependencies: ") + if verbose { + outputLogger.Println("") + outputLogger.Println(" Install command: '" + b.projectData.InstallCommand + "'") + } + if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand, verbose); err != nil { + return err + } + outputLogger.Println("Done.") + } + + // Check if there is a build command + var buildCommand string + switch b.projectData.OutputType { + case "dev": + buildCommand = b.projectData.DevCommand + default: + buildCommand = b.projectData.BuildCommand + } + if buildCommand == "" { + outputLogger.Println("No Build command. Skipping.") + // No - ignore + return nil + } + + outputLogger.Print("Compiling frontend: ") + cmd := strings.Split(b.projectData.BuildCommand, " ") + if verbose { + outputLogger.Println("") + outputLogger.Println(" Build command: '" + strings.Join(cmd, " ") + "'") + } + stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...) + if verbose || err != nil { + for _, l := range strings.Split(stdout, "\n") { + fmt.Printf(" %s\n", l) + } + for _, l := range strings.Split(stderr, "\n") { + fmt.Printf(" %s\n", l) + } + } + if err != nil { + return err + } + + outputLogger.Println("Done.") + return nil +} + +// ExtractAssets gets the assets from the index.html file +func (b *BaseBuilder) ExtractAssets() (*html.AssetBundle, error) { + + // Read in html + //return html.NewAssetBundle(b.projectData.HTML) + return nil, nil +} + +func upsertEnv(env []string, key string, update func(v string) string) []string { + newEnv := make([]string, len(env), len(env)+1) + found := false + for i := range env { + if strings.HasPrefix(env[i], key+"=") { + eqIndex := strings.Index(env[i], "=") + val := env[i][eqIndex+1:] + newEnv[i] = fmt.Sprintf("%s=%v", key, update(val)) + found = true + continue + } + newEnv[i] = env[i] + } + if !found { + newEnv = append(newEnv, fmt.Sprintf("%s=%v", key, update(""))) + } + return newEnv +} diff --git a/v2/pkg/commands/build/base_test.go b/v2/pkg/commands/build/base_test.go new file mode 100644 index 000000000..1f3c84cd2 --- /dev/null +++ b/v2/pkg/commands/build/base_test.go @@ -0,0 +1,31 @@ +package build + +import "testing" + +func TestUpdateEnv(t *testing.T) { + + env := []string{"one=1", "two=a=b", "three="} + newEnv := upsertEnv(env, "two", func(v string) string { + return v + "+added" + }) + newEnv = upsertEnv(newEnv, "newVar", func(v string) string { + return "added" + }) + newEnv = upsertEnv(newEnv, "three", func(v string) string { + return "3" + }) + + if len(newEnv) != 4 { + t.Errorf("expected: 4, got: %d", len(newEnv)) + } + if newEnv[1] != "two=a=b+added" { + t.Errorf("expected: \"two=a=b+added\", got: %q", newEnv[1]) + } + if newEnv[2] != "three=3" { + t.Errorf("expected: \"three=3\", got: %q", newEnv[2]) + } + if newEnv[3] != "newVar=added" { + t.Errorf("expected: \"newVar=added\", got: %q", newEnv[3]) + } + +} diff --git a/v2/pkg/commands/build/build.go b/v2/pkg/commands/build/build.go new file mode 100644 index 000000000..9e2798a1b --- /dev/null +++ b/v2/pkg/commands/build/build.go @@ -0,0 +1,217 @@ +package build + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" + + "github.com/wailsapp/wails/v2/internal/fs" + + "github.com/wailsapp/wails/v2/internal/shell" + + "github.com/wailsapp/wails/v2/internal/project" + "github.com/wailsapp/wails/v2/pkg/clilogger" +) + +// Mode is the type used to indicate the build modes +type Mode int + +const ( + // Dev mode + Dev Mode = iota + // Production mode + Production +) + +// Options contains all the build options as well as the project data +type Options struct { + LDFlags string // Optional flags to pass to linker + UserTags []string // Tags to pass to the Go compiler + Logger *clilogger.CLILogger // All output to the logger + OutputType string // EG: desktop, server.... + Mode Mode // release or dev + ProjectData *project.Project // The project data + Pack bool // Create a package for the app after building + Platform string // The platform to build for + Arch string // The architecture to build for + Compiler string // The compiler command to use + IgnoreFrontend bool // Indicates if the frontend does not need building + OutputFile string // Override the output filename + BuildDirectory string // Directory to use for building the application + CleanBuildDirectory bool // Indicates if the build directory should be cleaned before building + CompiledBinary string // Fully qualified path to the compiled binary + KeepAssets bool // Keep the generated assets/files + Verbosity int // Verbosity level (0 - silent, 1 - default, 2 - verbose) + Compress bool // Compress the final binary + CompressFlags string // Flags to pass to UPX + WebView2Strategy string // WebView2 installer strategy + RunDelve bool // Indicates if we should run delve after the build + WailsJSDir string // Directory to generate the wailsjs module + ForceBuild bool // Force +} + +// Build the project! +func Build(options *Options) (string, error) { + + // Extract logger + outputLogger := options.Logger + + // Get working directory + cwd, err := os.Getwd() + if err != nil { + return "", err + } + + // Load project + projectData, err := project.Load(cwd) + if err != nil { + return "", err + } + options.ProjectData = projectData + + // Add default path if it doesn't exist + if projectData.Path == "" { + projectData.Path = cwd + } + + // Set build directory + options.BuildDirectory = filepath.Join(options.ProjectData.Path, "build", "bin") + + // Save the project type + projectData.OutputType = options.OutputType + + // Create builder + var builder Builder + + switch projectData.OutputType { + case "desktop": + builder = newDesktopBuilder(options) + case "hybrid": + builder = newHybridBuilder(options) + case "server": + builder = newServerBuilder(options) + case "dev": + builder = newDesktopBuilder(options) + default: + return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType) + } + + // Set up our clean up method + defer builder.CleanUp() + + // Initialise Builder + builder.SetProjectData(projectData) + + if !options.IgnoreFrontend || options.ForceBuild { + err = builder.BuildFrontend(outputLogger) + if err != nil { + return "", err + } + } + + // Build the base assets + //err = builder.BuildAssets(options) + //if err != nil { + // return "", err + //} + + // If we are building for windows, we will need to generate the asset bundle before + // compilation. This will be a .syso file in the project root + if options.Pack && options.Platform == "windows" { + outputLogger.Print("Generating bundle assets: ") + err := packageApplication(options) + if err != nil { + return "", err + } + outputLogger.Println("Done.") + + // When we finish, we will want to remove the syso file + defer func() { + err := os.Remove(filepath.Join(options.ProjectData.Path, options.ProjectData.Name+"-res.syso")) + if err != nil { + log.Fatal(err) + } + }() + } + + // Compile the application + outputLogger.Print("Compiling application: ") + + if options.Platform == "darwin" && options.Arch == "universal" { + outputFile := builder.OutputFilename(options) + amd64Filename := outputFile + "-amd64" + arm64Filename := outputFile + "-arm64" + + // Build amd64 first + options.Arch = "amd64" + options.OutputFile = amd64Filename + options.CleanBuildDirectory = false + if options.Verbosity == VERBOSE { + println() + println(" Building AMD64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile)) + } + err = builder.CompileProject(options) + if err != nil { + return "", err + } + // Build arm64 + options.Arch = "arm64" + options.OutputFile = arm64Filename + options.CleanBuildDirectory = false + if options.Verbosity == VERBOSE { + println(" Building ARM64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile)) + } + err = builder.CompileProject(options) + if err != nil { + return "", err + } + // Run lipo + if options.Verbosity == VERBOSE { + println(" Running lipo: ", "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename) + } + _, stderr, err := shell.RunCommand(options.BuildDirectory, "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename) + if err != nil { + return "", fmt.Errorf("%s - %s", err.Error(), stderr) + } + // Remove temp binaries + err = fs.DeleteFile(filepath.Join(options.BuildDirectory, amd64Filename)) + if err != nil { + return "", err + } + err = fs.DeleteFile(filepath.Join(options.BuildDirectory, arm64Filename)) + if err != nil { + return "", err + } + projectData.OutputFilename = outputFile + options.CompiledBinary = filepath.Join(options.BuildDirectory, outputFile) + } else { + err = builder.CompileProject(options) + if err != nil { + return "", err + } + } + + // Do we need to pack the app for non-windows? + if options.Pack && options.Platform != "windows" { + + outputLogger.Print("Packaging application: ") + + // TODO: Allow cross platform build + err = packageProject(options, runtime.GOOS) + if err != nil { + return "", err + } + outputLogger.Println("Done.") + } + + // Post compilation tasks + err = builder.PostCompilation(options) + if err != nil { + return "", err + } + + return options.CompiledBinary, nil + +} diff --git a/v2/pkg/commands/build/builder.go b/v2/pkg/commands/build/builder.go new file mode 100644 index 000000000..81dd5d307 --- /dev/null +++ b/v2/pkg/commands/build/builder.go @@ -0,0 +1,18 @@ +package build + +import ( + "github.com/wailsapp/wails/v2/internal/project" + "github.com/wailsapp/wails/v2/pkg/clilogger" +) + +// Builder defines a builder that can build Wails applications +type Builder interface { + SetProjectData(projectData *project.Project) + BuildAssets(*Options) error + BuildFrontend(*clilogger.CLILogger) error + BuildRuntime(*Options) error + CompileProject(*Options) error + OutputFilename(*Options) string + PostCompilation(*Options) error + CleanUp() +} diff --git a/v2/pkg/commands/build/desktop.go b/v2/pkg/commands/build/desktop.go new file mode 100644 index 000000000..76623c01a --- /dev/null +++ b/v2/pkg/commands/build/desktop.go @@ -0,0 +1,166 @@ +package build + +import ( + "fmt" + "github.com/wailsapp/wails/v2/pkg/buildassets" + "io/ioutil" + "path/filepath" + + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/internal/html" +) + +// DesktopBuilder builds applications for the desktop +type DesktopBuilder struct { + *BaseBuilder +} + +func newDesktopBuilder(options *Options) *DesktopBuilder { + return &DesktopBuilder{ + BaseBuilder: NewBaseBuilder(options), + } +} + +// BuildAssets builds the assets for the desktop application +func (d *DesktopBuilder) BuildAssets(options *Options) error { + + // Check assets directory exists + if !fs.DirExists(options.ProjectData.BuildDir) { + // Path to default assets + err := buildassets.Install(options.ProjectData.Path, options.ProjectData.Name) + if err != nil { + return err + } + } + + // We only build assets for cgo builds + //userTags := slicer.String(options.UserTags) + //if userTags.Contains("cgo") { + // // Get a list of assets from the HTML + // assets, err := d.BaseBuilder.ExtractAssets() + // if err != nil { + // return err + // } + // + // // Build base assets (HTML/JS/CSS/etc) + // err = d.BuildBaseAssets(assets, options) + // if err != nil { + // return err + // } + //} + + return nil +} + +// BuildBaseAssets builds the assets for the desktop application +func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Options) error { + var err error + + outputLogger := options.Logger + outputLogger.Print("Building assets: ") + + // Get target asset directory + assetDir, err := fs.RelativeToCwd("build") + if err != nil { + return err + } + + // Make dir if it doesn't exist + if !fs.DirExists(assetDir) { + err := fs.Mkdir(assetDir) + if err != nil { + return err + } + } + + // Dump assets as C + assetsFile, err := assets.WriteToCFile(assetDir) + if err != nil { + return err + } + d.addFileToDelete(assetsFile) + + // Process Icon + err = d.processApplicationIcon(assetDir) + if err != nil { + return err + } + + // Process Tray Icons + err = d.processTrayIcons(assetDir, options) + if err != nil { + return err + } + + // Process Dialog Icons + err = d.processDialogIcons(assetDir, options) + if err != nil { + return err + } + + outputLogger.Println("Done.") + + return nil +} + +// processApplicationIcon will copy a default icon if one doesn't exist, then, if +// needed, will compile the icon +func (d *DesktopBuilder) processApplicationIcon(assetDir string) error { + + // Copy default icon if one doesn't exist + iconFile := filepath.Join(d.projectData.BuildDir, "appicon.png") + if !fs.FileExists(iconFile) { + err := buildassets.RegenerateAppIcon(iconFile) + if err != nil { + return err + } + } + + // Compile Icon + return d.compileIcon(assetDir, iconFile) +} + +// BuildRuntime builds the Wails javascript runtime and then converts it into a C file +func (d *DesktopBuilder) BuildRuntime(options *Options) error { + + outputLogger := options.Logger + + sourceDir := fs.RelativePath("../../../internal/runtime/js") + + if err := d.NpmInstall(sourceDir, options.Verbosity == VERBOSE); err != nil { + return err + } + + outputLogger.Print("Embedding Runtime: ") + envvars := []string{"WAILSPLATFORM=" + options.Platform} + if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil { + return err + } + + wailsJS := fs.RelativePath("../../../internal/runtime/assets/desktop.js") + runtimeData, err := ioutil.ReadFile(wailsJS) + if err != nil { + return err + } + outputLogger.Println("done.") + + // Convert to C structure + runtimeC := ` +// runtime.c (c) 2019-Present Lea Anthony. +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file was auto-generated. DO NOT MODIFY. +const unsigned char runtime[]={` + for _, b := range runtimeData { + runtimeC += fmt.Sprintf("0x%x, ", b) + } + runtimeC += "0x00};" + + // Save file + outputFile := fs.RelativePath("../../../internal/ffenestri/runtime.c") + + if err := ioutil.WriteFile(outputFile, []byte(runtimeC), 0600); err != nil { + return err + } + + return nil +} diff --git a/v2/pkg/commands/build/desktop_darwin.go b/v2/pkg/commands/build/desktop_darwin.go new file mode 100644 index 000000000..bc084f4f4 --- /dev/null +++ b/v2/pkg/commands/build/desktop_darwin.go @@ -0,0 +1,211 @@ +// +build darwin + +package build + +import ( + "fmt" + "io/ioutil" + "log" + "path/filepath" + "strconv" + "strings" + + "github.com/leaanthony/slicer" + "github.com/wailsapp/wails/v2/internal/fs" +) + +func (d *DesktopBuilder) convertToHexLiteral(bytes []byte) string { + result := "" + for _, b := range bytes { + result += fmt.Sprintf("0x%x, ", b) + } + return result +} + +// compileIcon will compile the icon found at /icon.png into the application +func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error { + return nil +} + +// We will compile all tray icons found at /assets/trayicons/*.png into the application +func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) error { + + var err error + + // Get all the tray icon filenames + trayIconDirectory := filepath.Join(options.ProjectData.BuildDir, "tray") + + // If the directory doesn't exist, create it + if !fs.DirExists(trayIconDirectory) { + err = fs.MkDirs(trayIconDirectory) + if err != nil { + return err + } + } + + var trayIconFilenames []string + trayIconFilenames, err = filepath.Glob(trayIconDirectory + "/*.png") + if err != nil { + log.Fatal(err) + return err + } + + // Setup target + targetFilename := "trayicons" + targetFile := filepath.Join(assetDir, targetFilename+".h") + d.addFileToDelete(targetFile) + + var dataBytes []byte + + // Use a strings builder + var cdata strings.Builder + + // Write header + header := `// trayicons.h +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. +// This file was auto-generated. DO NOT MODIFY. + +` + cdata.WriteString(header) + + var variableList slicer.StringSlicer + + // Loop over icons + for count, filename := range trayIconFilenames { + + // Load the tray icon + dataBytes, err = ioutil.ReadFile(filename) + if err != nil { + return err + } + + iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + trayIconName := fmt.Sprintf("trayIcon%dName", count) + variableList.Add(trayIconName) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconName, d.convertToHexLiteral([]byte(iconname)))) + + trayIconLength := fmt.Sprintf("trayIcon%dLength", count) + variableList.Add(trayIconLength) + lengthAsString := strconv.Itoa(len(dataBytes)) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconLength, d.convertToHexLiteral([]byte(lengthAsString)))) + + trayIconData := fmt.Sprintf("trayIcon%dData", count) + variableList.Add(trayIconData) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", trayIconData)) + + // Convert each byte to hex + for _, b := range dataBytes { + cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + } + + cdata.WriteString("0x00 };\n") + } + + // Write out main trayIcons data + cdata.WriteString("const unsigned char *trayIcons[] = { ") + cdata.WriteString(variableList.Join(", ")) + if len(trayIconFilenames) > 0 { + cdata.WriteString(", ") + } + cdata.WriteString("0x00 };\n") + + err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + if err != nil { + return err + } + return nil +} + +// PostCompilation is called after the compilation step, if successful +func (d *DesktopBuilder) PostCompilation(options *Options) error { + return nil +} + +// We will compile all dialog icons found at /icons/dialog/*.png into the application +func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) error { + + var err error + + // Get all the dialog icon filenames + dialogIconDirectory := filepath.Join(options.ProjectData.BuildDir, "dialog") + var dialogIconFilenames []string + + // If the directory does not exist, create it + if !fs.DirExists(dialogIconDirectory) { + err = fs.MkDirs(dialogIconDirectory) + if err != nil { + return err + } + } + + dialogIconFilenames, err = filepath.Glob(dialogIconDirectory + "/*.png") + if err != nil { + log.Fatal(err) + return err + } + + // Setup target + targetFilename := "userdialogicons" + targetFile := filepath.Join(assetDir, targetFilename+".h") + d.addFileToDelete(targetFile) + + var dataBytes []byte + + // Use a strings builder + var cdata strings.Builder + + // Write header + header := `// userdialogicons.h +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. +// This file was auto-generated. DO NOT MODIFY. + +` + cdata.WriteString(header) + + var variableList slicer.StringSlicer + + // Loop over icons + for count, filename := range dialogIconFilenames { + + // Load the tray icon + dataBytes, err = ioutil.ReadFile(filename) + if err != nil { + return err + } + + iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + dialogIconName := fmt.Sprintf("userDialogIcon%dName", count) + variableList.Add(dialogIconName) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconName, d.convertToHexLiteral([]byte(iconname)))) + + dialogIconLength := fmt.Sprintf("userDialogIcon%dLength", count) + variableList.Add(dialogIconLength) + lengthAsString := strconv.Itoa(len(dataBytes)) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconLength, d.convertToHexLiteral([]byte(lengthAsString)))) + + dialogIconData := fmt.Sprintf("userDialogIcon%dData", count) + variableList.Add(dialogIconData) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", dialogIconData)) + + // Convert each byte to hex + for _, b := range dataBytes { + cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + } + + cdata.WriteString("0x00 };\n") + } + + // Write out main dialogIcons data + cdata.WriteString("const unsigned char *userDialogIcons[] = { ") + cdata.WriteString(variableList.Join(", ")) + if len(dialogIconFilenames) > 0 { + cdata.WriteString(", ") + } + cdata.WriteString("0x00 };\n") + + err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + if err != nil { + return err + } + return nil +} diff --git a/v2/pkg/commands/build/desktop_linux.go b/v2/pkg/commands/build/desktop_linux.go new file mode 100644 index 000000000..1d94e5ac6 --- /dev/null +++ b/v2/pkg/commands/build/desktop_linux.go @@ -0,0 +1,240 @@ +// +build linux + +package build + +import ( + "image/png" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/xyproto/xpm" +) + +// compileIcon will compile the icon found at /icon.png into the application +func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error { + + // Load icon into a databuffer + targetFilename := "icon" + targetFile := filepath.Join(assetDir, targetFilename+".h") + + d.addFileToDelete(targetFile) + + // Create a new XPM encoder + enc := xpm.NewEncoder(targetFilename) + + // Open the PNG file + f, err := os.Open(iconFile) + if err != nil { + return err + } + m, err := png.Decode(f) + if err != nil { + return err + } + err = f.Close() + if err != nil { + return err + } + + var buf strings.Builder + + // Generate and output the XPM data + err = enc.Encode(&buf, m) + if err != nil { + return err + } + + // Massage the output so we can extern reference it + output := buf.String() + output = strings.Replace(output, "static char", "const char", 1) + + // save icon.c + err = ioutil.WriteFile(targetFile, []byte(output), 0755) + + return err +} + +// We will compile all tray icons found at /assets/trayicons/*.png into the application +func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) error { + // + // var err error + // + // // Get all the tray icon filenames + // trayIconDirectory := filepath.Join(options.ProjectData.BuildDir, "tray") + // + // // If the directory doesn't exist, create it + // if !fs.DirExists(trayIconDirectory) { + // err = fs.MkDirs(trayIconDirectory) + // if err != nil { + // return err + // } + // } + // + // var trayIconFilenames []string + // trayIconFilenames, err = filepath.Glob(trayIconDirectory + "/*.png") + // if err != nil { + // log.Fatal(err) + // return err + // } + // + // // Setup target + // targetFilename := "trayicons" + // targetFile := filepath.Join(assetDir, targetFilename+".h") + // d.addFileToDelete(targetFile) + // + // var dataBytes []byte + // + // // Use a strings builder + // var cdata strings.Builder + // + // // Write header + // header := `// trayicons.h + //// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. + //// This file was auto-generated. DO NOT MODIFY. + // + //` + // cdata.WriteString(header) + // + // var variableList slicer.StringSlicer + // + // // Loop over icons + // for count, filename := range trayIconFilenames { + // + // // Load the tray icon + // dataBytes, err = ioutil.ReadFile(filename) + // if err != nil { + // return err + // } + // + // iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + // trayIconName := fmt.Sprintf("trayIcon%dName", count) + // variableList.Add(trayIconName) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconName, d.convertToHexLiteral([]byte(iconname)))) + // + // trayIconLength := fmt.Sprintf("trayIcon%dLength", count) + // variableList.Add(trayIconLength) + // lengthAsString := strconv.Itoa(len(dataBytes)) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconLength, d.convertToHexLiteral([]byte(lengthAsString)))) + // + // trayIconData := fmt.Sprintf("trayIcon%dData", count) + // variableList.Add(trayIconData) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", trayIconData)) + // + // // Convert each byte to hex + // for _, b := range dataBytes { + // cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + // } + // + // cdata.WriteString("0x00 };\n") + // } + // + // // Write out main trayIcons data + // cdata.WriteString("const unsigned char *trayIcons[] = { ") + // cdata.WriteString(variableList.Join(", ")) + // if len(trayIconFilenames) > 0 { + // cdata.WriteString(", ") + // } + // cdata.WriteString("0x00 };\n") + // + // err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + // if err != nil { + // return err + // } + return nil +} + +// We will compile all dialog icons found at /icons/dialog/*.png into the application +func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) error { + + // var err error + // + // // Get all the dialog icon filenames + // dialogIconDirectory := filepath.Join(options.ProjectData.BuildDir, "dialog") + // var dialogIconFilenames []string + // + // // If the directory does not exist, create it + // if !fs.DirExists(dialogIconDirectory) { + // err = fs.MkDirs(dialogIconDirectory) + // if err != nil { + // return err + // } + // } + // + // dialogIconFilenames, err = filepath.Glob(dialogIconDirectory + "/*.png") + // if err != nil { + // log.Fatal(err) + // return err + // } + // + // // Setup target + // targetFilename := "userdialogicons" + // targetFile := filepath.Join(assetDir, targetFilename+".h") + // d.addFileToDelete(targetFile) + // + // var dataBytes []byte + // + // // Use a strings builder + // var cdata strings.Builder + // + // // Write header + // header := `// userdialogicons.h + //// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. + //// This file was auto-generated. DO NOT MODIFY. + // + //` + // cdata.WriteString(header) + // + // var variableList slicer.StringSlicer + // + // // Loop over icons + // for count, filename := range dialogIconFilenames { + // + // // Load the tray icon + // dataBytes, err = ioutil.ReadFile(filename) + // if err != nil { + // return err + // } + // + // iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + // dialogIconName := fmt.Sprintf("userDialogIcon%dName", count) + // variableList.Add(dialogIconName) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconName, d.convertToHexLiteral([]byte(iconname)))) + // + // dialogIconLength := fmt.Sprintf("userDialogIcon%dLength", count) + // variableList.Add(dialogIconLength) + // lengthAsString := strconv.Itoa(len(dataBytes)) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconLength, d.convertToHexLiteral([]byte(lengthAsString)))) + // + // dialogIconData := fmt.Sprintf("userDialogIcon%dData", count) + // variableList.Add(dialogIconData) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", dialogIconData)) + // + // // Convert each byte to hex + // for _, b := range dataBytes { + // cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + // } + // + // cdata.WriteString("0x00 };\n") + // } + // + // // Write out main dialogIcons data + // cdata.WriteString("const unsigned char *userDialogIcons[] = { ") + // cdata.WriteString(variableList.Join(", ")) + // if len(dialogIconFilenames) > 0 { + // cdata.WriteString(", ") + // } + // cdata.WriteString("0x00 };\n") + // + // err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + // if err != nil { + // return err + // } + return nil +} + +// PostCompilation is called after the compilation step, if successful +func (d *DesktopBuilder) PostCompilation(options *Options) error { + return nil +} diff --git a/v2/pkg/commands/build/desktop_windows.go b/v2/pkg/commands/build/desktop_windows.go new file mode 100644 index 000000000..afb2e011f --- /dev/null +++ b/v2/pkg/commands/build/desktop_windows.go @@ -0,0 +1,199 @@ +// +build windows + +package build + +// PostCompilation is called after the compilation step, if successful +func (d *DesktopBuilder) PostCompilation(options *Options) error { + // Dump the DLLs + //userTags := slicer.String(options.UserTags) + //if userTags.Contains("cgo") { + // err := os.WriteFile(filepath.Join(options.BuildDirectory, "WebView2Loader.dll"), x64.WebView2Loader, 0755) + // if err != nil { + // return err + // } + //} + return nil +} + +// We will compile all tray icons found at /assets/trayicons/*.png into the application +func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) error { + // + // var err error + // + // // Get all the tray icon filenames + // trayIconDirectory := filepath.Join(options.ProjectData.BuildDir, "tray") + // + // // If the directory doesn't exist, create it + // if !fs.DirExists(trayIconDirectory) { + // err = fs.MkDirs(trayIconDirectory) + // if err != nil { + // return err + // } + // } + // + // var trayIconFilenames []string + // trayIconFilenames, err = filepath.Glob(trayIconDirectory + "/*.png") + // if err != nil { + // log.Fatal(err) + // return err + // } + // + // // Setup target + // targetFilename := "trayicons" + // targetFile := filepath.Join(assetDir, targetFilename+".h") + // d.addFileToDelete(targetFile) + // + // var dataBytes []byte + // + // // Use a strings builder + // var cdata strings.Builder + // + // // Write header + // header := `// trayicons.h + //// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. + //// This file was auto-generated. DO NOT MODIFY. + // + //` + // cdata.WriteString(header) + // + // var variableList slicer.StringSlicer + // + // // Loop over icons + // for count, filename := range trayIconFilenames { + // + // // Load the tray icon + // dataBytes, err = ioutil.ReadFile(filename) + // if err != nil { + // return err + // } + // + // iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + // trayIconName := fmt.Sprintf("trayIcon%dName", count) + // variableList.Add(trayIconName) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconName, d.convertToHexLiteral([]byte(iconname)))) + // + // trayIconLength := fmt.Sprintf("trayIcon%dLength", count) + // variableList.Add(trayIconLength) + // lengthAsString := strconv.Itoa(len(dataBytes)) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconLength, d.convertToHexLiteral([]byte(lengthAsString)))) + // + // trayIconData := fmt.Sprintf("trayIcon%dData", count) + // variableList.Add(trayIconData) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", trayIconData)) + // + // // Convert each byte to hex + // for _, b := range dataBytes { + // cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + // } + // + // cdata.WriteString("0x00 };\n") + // } + // + // // Write out main trayIcons data + // cdata.WriteString("const unsigned char *trayIcons[] = { ") + // cdata.WriteString(variableList.Join(", ")) + // if len(trayIconFilenames) > 0 { + // cdata.WriteString(", ") + // } + // cdata.WriteString("0x00 };\n") + // + // err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + // if err != nil { + // return err + // } + return nil +} + +// compileIcon will compile the icon found at /icon.png into the application +func (d *DesktopBuilder) compileIcon(assetDir string, iconFile string) error { + return nil +} + +// We will compile all dialog icons found at /icons/dialog/*.png into the application +func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) error { + + // var err error + // + // // Get all the dialog icon filenames + // dialogIconDirectory := filepath.Join(options.ProjectData.BuildDir, "dialog") + // var dialogIconFilenames []string + // + // // If the directory does not exist, create it + // if !fs.DirExists(dialogIconDirectory) { + // err = fs.MkDirs(dialogIconDirectory) + // if err != nil { + // return err + // } + // } + // + // dialogIconFilenames, err = filepath.Glob(dialogIconDirectory + "/*.png") + // if err != nil { + // log.Fatal(err) + // return err + // } + // + // // Setup target + // targetFilename := "userdialogicons" + // targetFile := filepath.Join(assetDir, targetFilename+".h") + // d.addFileToDelete(targetFile) + // + // var dataBytes []byte + // + // // Use a strings builder + // var cdata strings.Builder + // + // // Write header + // header := `// userdialogicons.h + //// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. + //// This file was auto-generated. DO NOT MODIFY. + // + //` + // cdata.WriteString(header) + // + // var variableList slicer.StringSlicer + // + // // Loop over icons + // for count, filename := range dialogIconFilenames { + // + // // Load the tray icon + // dataBytes, err = ioutil.ReadFile(filename) + // if err != nil { + // return err + // } + // + // iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + // dialogIconName := fmt.Sprintf("userDialogIcon%dName", count) + // variableList.Add(dialogIconName) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconName, d.convertToHexLiteral([]byte(iconname)))) + // + // dialogIconLength := fmt.Sprintf("userDialogIcon%dLength", count) + // variableList.Add(dialogIconLength) + // lengthAsString := strconv.Itoa(len(dataBytes)) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconLength, d.convertToHexLiteral([]byte(lengthAsString)))) + // + // dialogIconData := fmt.Sprintf("userDialogIcon%dData", count) + // variableList.Add(dialogIconData) + // cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", dialogIconData)) + // + // // Convert each byte to hex + // for _, b := range dataBytes { + // cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + // } + // + // cdata.WriteString("0x00 };\n") + // } + // + // // Write out main dialogIcons data + // cdata.WriteString("const unsigned char *userDialogIcons[] = { ") + // cdata.WriteString(variableList.Join(", ")) + // if len(dialogIconFilenames) > 0 { + // cdata.WriteString(", ") + // } + // cdata.WriteString("0x00 };\n") + // + // err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + // if err != nil { + // return err + // } + return nil +} diff --git a/v2/pkg/commands/build/hybrid.go b/v2/pkg/commands/build/hybrid.go new file mode 100644 index 000000000..d21c78e60 --- /dev/null +++ b/v2/pkg/commands/build/hybrid.go @@ -0,0 +1,95 @@ +package build + +import ( + "github.com/wailsapp/wails/v2/internal/project" + "github.com/wailsapp/wails/v2/pkg/clilogger" +) + +// HybridBuilder builds applications as a server +type HybridBuilder struct { + *BaseBuilder + desktop *DesktopBuilder + server *ServerBuilder +} + +func newHybridBuilder(options *Options) Builder { + result := &HybridBuilder{ + BaseBuilder: NewBaseBuilder(options), + desktop: newDesktopBuilder(options), + server: newServerBuilder(options), + } + return result +} + +// BuildAssets builds the assets for the desktop application +func (b *HybridBuilder) BuildAssets(options *Options) error { + var err error + + // Build base assets (HTML/JS/CSS/etc) + err = b.BuildBaseAssets(options) + if err != nil { + return err + } + return nil +} + +// BuildFrontend builds the assets for the desktop application +func (b *HybridBuilder) BuildFrontend(_ *clilogger.CLILogger) error { + panic("To be implemented") + return nil +} + +// BuildAssets builds the assets for the desktop application +func (b *HybridBuilder) BuildBaseAssets(options *Options) error { + + assets, err := b.BaseBuilder.ExtractAssets() + if err != nil { + return err + } + + err = b.desktop.BuildBaseAssets(assets, options) + if err != nil { + return err + } + + err = b.server.BuildBaseAssets(assets) + if err != nil { + return err + } + + return nil +} + +func (b *HybridBuilder) BuildRuntime(options *Options) error { + err := b.desktop.BuildRuntime(options) + if err != nil { + return err + } + + err = b.server.BuildRuntime(options) + if err != nil { + return err + } + + return nil +} + +func (b *HybridBuilder) SetProjectData(projectData *project.Project) { + b.BaseBuilder.SetProjectData(projectData) + b.desktop.SetProjectData(projectData) + b.server.SetProjectData(projectData) +} + +func (b *HybridBuilder) CompileProject(options *Options) error { + return b.BaseBuilder.CompileProject(options) +} + +func (b *HybridBuilder) CleanUp() { + b.desktop.CleanUp() + b.server.CleanUp() +} + +// PostCompilation is called after the compilation step, if successful +func (s *HybridBuilder) PostCompilation(_ *Options) error { + return nil +} diff --git a/v2/pkg/commands/build/internal/packager/darwin/Info.plist b/v2/pkg/commands/build/internal/packager/darwin/Info.plist new file mode 100644 index 000000000..8e67cb18f --- /dev/null +++ b/v2/pkg/commands/build/internal/packager/darwin/Info.plist @@ -0,0 +1,14 @@ + + + CFBundlePackageTypeAPPL + CFBundleName{{.Title}} + CFBundleExecutable{{.Title}} + CFBundleIdentifiercom.wails.{{.Title}} + CFBundleVersion1.0.0 + CFBundleGetInfoStringBuilt using Wails (https://wails.app) + CFBundleShortVersionString1.0.0 + CFBundleIconFileiconfile + LSMinimumSystemVersion10.13.0 + NSHighResolutionCapabletrue + NSHumanReadableCopyrightCopyright......... + \ No newline at end of file diff --git a/v2/pkg/commands/build/internal/packager/icon1024.png b/v2/pkg/commands/build/internal/packager/icon1024.png new file mode 100644 index 000000000..a3ad26ce7 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon1024.png differ diff --git a/v2/pkg/commands/build/internal/packager/icon128.png b/v2/pkg/commands/build/internal/packager/icon128.png new file mode 100644 index 000000000..2ddf98679 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon128.png differ diff --git a/v2/pkg/commands/build/internal/packager/icon256.png b/v2/pkg/commands/build/internal/packager/icon256.png new file mode 100644 index 000000000..0f4235b84 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon256.png differ diff --git a/v2/pkg/commands/build/internal/packager/icon32.png b/v2/pkg/commands/build/internal/packager/icon32.png new file mode 100644 index 000000000..a03607d66 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon32.png differ diff --git a/v2/pkg/commands/build/internal/packager/icon512.png b/v2/pkg/commands/build/internal/packager/icon512.png new file mode 100644 index 000000000..53c612c7b Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon512.png differ diff --git a/v2/pkg/commands/build/internal/packager/icon64.png b/v2/pkg/commands/build/internal/packager/icon64.png new file mode 100644 index 000000000..a2b304154 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icon64.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/README.md b/v2/pkg/commands/build/internal/packager/icons/dialog/README.md new file mode 100644 index 000000000..b9ddc2781 --- /dev/null +++ b/v2/pkg/commands/build/internal/packager/icons/dialog/README.md @@ -0,0 +1,3 @@ +# Default Dialog Icons + +This directory contains the default dialog icons. These are pre-compiled into a single C file (`defaultDialogIcons.c`) which resides in the `ffenestri` directory. If these icons are ever updated, then there is a need to run: `go run build.go` in this directory. This will generate a new `defaultDialogIcons.c` file in the `ffenestri` directory. \ No newline at end of file diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/build.go b/v2/pkg/commands/build/internal/packager/icons/dialog/build.go new file mode 100644 index 000000000..1d4f74f7a --- /dev/null +++ b/v2/pkg/commands/build/internal/packager/icons/dialog/build.go @@ -0,0 +1,99 @@ +package main + +import ( + "fmt" + "github.com/leaanthony/slicer" + "io/ioutil" + "log" + "path/filepath" + "strconv" + "strings" +) + +func convertToHexLiteral(bytes []byte) string { + result := "" + for _, b := range bytes { + result += fmt.Sprintf("0x%x, ", b) + } + return result +} + +func main() { + dialogIconFilenames, err := filepath.Glob("*.png") + if err != nil { + log.Fatal(err) + } + + // Build icons for Mac + err = buildMacIcons(dialogIconFilenames) + if err != nil { + log.Fatal(err) + } +} + +func buildMacIcons(dialogIconFilenames []string) error { + + // Setup target + targetFile := "../../../../../../../internal/ffenestri/defaultdialogicons_darwin.c" + + var dataBytes []byte + var err error + + // Use a strings builder + var cdata strings.Builder + + // Write header + header := `// defaultdialogicons_darwin.c +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. +// This file was auto-generated. DO NOT MODIFY. + +` + cdata.WriteString(header) + + var variableList slicer.StringSlicer + + // Loop over icons + for count, filename := range dialogIconFilenames { + + // Load the tray icon + dataBytes, err = ioutil.ReadFile(filename) + if err != nil { + log.Fatal(err) + } + + iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + dialogIconName := fmt.Sprintf("defaultDialogIcon%dName", count) + variableList.Add(dialogIconName) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconName, convertToHexLiteral([]byte(iconname)))) + + dialogIconLength := fmt.Sprintf("defaultDialogIcon%dLength", count) + variableList.Add(dialogIconLength) + lengthAsString := strconv.Itoa(len(dataBytes)) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconLength, convertToHexLiteral([]byte(lengthAsString)))) + + dialogIconData := fmt.Sprintf("defaultDialogIcon%dData", count) + variableList.Add(dialogIconData) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", dialogIconData)) + + // Convert each byte to hex + for _, b := range dataBytes { + cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + } + + cdata.WriteString("0x00 };\n") + } + + // Write out main dialogIcons data + cdata.WriteString("const unsigned char *defaultDialogIcons[] = { ") + cdata.WriteString(variableList.Join(", ")) + if len(dialogIconFilenames) > 0 { + cdata.WriteString(", ") + } + cdata.WriteString("0x00 };\n") + + err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + if err != nil { + log.Fatal(err) + } + return nil +} diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/info-dark.png b/v2/pkg/commands/build/internal/packager/icons/dialog/info-dark.png new file mode 100644 index 000000000..9ff6655ee Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/info-dark.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/info-dark2x.png b/v2/pkg/commands/build/internal/packager/icons/dialog/info-dark2x.png new file mode 100644 index 000000000..fcdf8006a Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/info-dark2x.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/info-light.png b/v2/pkg/commands/build/internal/packager/icons/dialog/info-light.png new file mode 100644 index 000000000..1fb32e8a9 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/info-light.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/info-light2x.png b/v2/pkg/commands/build/internal/packager/icons/dialog/info-light2x.png new file mode 100644 index 000000000..874b2d301 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/info-light2x.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/question-dark.png b/v2/pkg/commands/build/internal/packager/icons/dialog/question-dark.png new file mode 100644 index 000000000..c2387420e Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/question-dark.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/question-dark2x.png b/v2/pkg/commands/build/internal/packager/icons/dialog/question-dark2x.png new file mode 100644 index 000000000..86ea1b037 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/question-dark2x.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/question-light.png b/v2/pkg/commands/build/internal/packager/icons/dialog/question-light.png new file mode 100644 index 000000000..0d3b6ba02 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/question-light.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/question-light2x.png b/v2/pkg/commands/build/internal/packager/icons/dialog/question-light2x.png new file mode 100644 index 000000000..fcd21569f Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/question-light2x.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/warning-dark.png b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-dark.png new file mode 100644 index 000000000..db432321b Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-dark.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/warning-dark2x.png b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-dark2x.png new file mode 100644 index 000000000..3325d22d2 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-dark2x.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/warning-light.png b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-light.png new file mode 100644 index 000000000..cf421a171 Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-light.png differ diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/warning-light2x.png b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-light2x.png new file mode 100644 index 000000000..ff092f6cd Binary files /dev/null and b/v2/pkg/commands/build/internal/packager/icons/dialog/warning-light2x.png differ diff --git a/v2/pkg/commands/build/internal/packager/linux/app.desktop b/v2/pkg/commands/build/internal/packager/linux/app.desktop new file mode 100644 index 000000000..59f0456d2 --- /dev/null +++ b/v2/pkg/commands/build/internal/packager/linux/app.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Exec={{.Name}} +Name={{.Name}} +Icon={{.Name}} +Categories=Utility; \ No newline at end of file diff --git a/v2/pkg/commands/build/packager.go b/v2/pkg/commands/build/packager.go new file mode 100644 index 000000000..9c1f2bd3f --- /dev/null +++ b/v2/pkg/commands/build/packager.go @@ -0,0 +1,67 @@ +package build + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + + "github.com/wailsapp/wails/v2/internal/fs" +) + +// PackageProject packages the application +func packageProject(options *Options, platform string) error { + + var err error + switch platform { + case "darwin", "windows": + err = packageApplication(options) + default: + err = fmt.Errorf("packing not supported for %s yet", platform) + } + + if err != nil { + return err + } + + return nil +} + +// cleanBuildDirectory will remove an existing build directory and recreate it +func cleanBuildDirectory(options *Options) error { + + buildDirectory := options.BuildDirectory + + // Clear out old builds + if fs.DirExists(buildDirectory) { + err := os.RemoveAll(buildDirectory) + if err != nil { + return err + } + } + + // Create clean directory + err := os.MkdirAll(buildDirectory, 0700) + if err != nil { + return err + } + + return nil +} + +// Gets (and creates) the build base directory +func getBuildBaseDirectory(options *Options) (string, error) { + buildDirectory := filepath.Join(options.ProjectData.Path, "build") + if !fs.DirExists(buildDirectory) { + err := os.MkdirAll(buildDirectory, 0700) + if err != nil { + return "", err + } + } + return buildDirectory, nil +} + +// Gets the platform dependent package assets directory +func getPackageAssetsDirectory() string { + return fs.RelativePath("internal/packager", runtime.GOOS) +} diff --git a/v2/pkg/commands/build/packager_darwin.go b/v2/pkg/commands/build/packager_darwin.go new file mode 100644 index 000000000..76aab0002 --- /dev/null +++ b/v2/pkg/commands/build/packager_darwin.go @@ -0,0 +1,181 @@ +package build + +import ( + "bytes" + "image" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" + "text/template" + + "github.com/wailsapp/wails/v2/pkg/buildassets" + + "github.com/jackmordaunt/icns" + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/internal/fs" +) + +func packageApplication(options *Options) error { + + var err error + + // Create directory structure + bundlename := options.ProjectData.Name + ".app" + + contentsDirectory := filepath.Join(options.BuildDirectory, bundlename, "/Contents") + exeDir := filepath.Join(contentsDirectory, "/MacOS") + err = fs.MkDirs(exeDir, 0755) + if err != nil { + return err + } + resourceDir := filepath.Join(contentsDirectory, "/Resources") + err = fs.MkDirs(resourceDir, 0755) + if err != nil { + return err + } + // Copy binary + packedBinaryPath := filepath.Join(exeDir, options.ProjectData.Name) + err = fs.MoveFile(options.CompiledBinary, packedBinaryPath) + if err != nil { + return errors.Wrap(err, "Cannot move file: "+options.ProjectData.OutputFilename) + } + + // Generate Info.plist + err = processPList(options, contentsDirectory) + if err != nil { + return err + } + + // Generate Icons + err = processApplicationIcon(resourceDir, options.ProjectData.BuildDir) + if err != nil { + return err + } + + options.CompiledBinary = packedBinaryPath + + return nil +} + +func processPList(options *Options, contentsDirectory string) error { + + // Check if plist already exists in project dir + plistFile := filepath.Join(options.ProjectData.BuildDir, "darwin", "Info.plist") + + // If the file doesn't exist, generate it + if !fs.FileExists(plistFile) { + err := generateDefaultPlist(options, plistFile) + if err != nil { + return err + } + } + + // Copy it to the contents directory + targetFile := filepath.Join(contentsDirectory, "Info.plist") + return fs.CopyFile(plistFile, targetFile) +} + +func generateDefaultPlist(options *Options, targetPlistFile string) error { + name := defaultString(options.ProjectData.Name, "WailsTest") + exe := defaultString(options.OutputFile, name) + version := "1.0.0" + author := defaultString(options.ProjectData.Author.Name, "Anonymous") + packageID := strings.Join([]string{"wails", name}, ".") + plistData := newPlistData(name, exe, packageID, version, author) + + tmpl := template.New("infoPlist") + plistTemplate := fs.RelativePath("./internal/packager/darwin/Info.plist") + infoPlist, err := ioutil.ReadFile(plistTemplate) + if err != nil { + return errors.Wrap(err, "Cannot open plist template") + } + _, err = tmpl.Parse(string(infoPlist)) + if err != nil { + return err + } + // Write the template to a buffer + var tpl bytes.Buffer + err = tmpl.Execute(&tpl, plistData) + if err != nil { + return err + } + + // Create the directory if it doesn't exist + err = fs.MkDirs(filepath.Dir(targetPlistFile)) + if err != nil { + return err + } + // Save the file + return ioutil.WriteFile(targetPlistFile, tpl.Bytes(), 0644) +} + +func defaultString(val string, defaultVal string) string { + if val != "" { + return val + } + return defaultVal +} + +type plistData struct { + Title string + Exe string + PackageID string + Version string + Author string +} + +func newPlistData(title, exe, packageID, version, author string) *plistData { + return &plistData{ + Title: title, + Exe: exe, + Version: version, + PackageID: packageID, + Author: author, + } +} + +func processApplicationIcon(resourceDir string, iconsDir string) (err error) { + + appIcon := filepath.Join(iconsDir, "appicon.png") + + // Install default icon if one doesn't exist + if !fs.FileExists(appIcon) { + // No - Install default icon + err = buildassets.RegenerateAppIcon(appIcon) + if err != nil { + return + } + } + + tgtBundle := path.Join(resourceDir, "iconfile.icns") + imageFile, err := os.Open(appIcon) + if err != nil { + return err + } + + defer func() { + err = imageFile.Close() + if err == nil { + return + } + }() + srcImg, _, err := image.Decode(imageFile) + if err != nil { + return err + + } + dest, err := os.Create(tgtBundle) + if err != nil { + return err + + } + defer func() { + err = dest.Close() + if err == nil { + return + } + }() + return icns.Encode(dest, srcImg) +} diff --git a/v2/pkg/commands/build/packager_linux.go b/v2/pkg/commands/build/packager_linux.go new file mode 100644 index 000000000..61436eb70 --- /dev/null +++ b/v2/pkg/commands/build/packager_linux.go @@ -0,0 +1,113 @@ +package build + +func packageApplication(_ *Options) error { + // + //// Check we have AppImage tools + // + //// Create AppImage build directory + //buildDirectory, err := getApplicationBuildDirectory(options, "linux") + //if err != nil { + // return err + //} + // + //defer deleteLinuxPackFiles(buildDirectory) + // + //// Get the name of the application and ensure we lower+kebab case it + //name := filepath.Base(options.ProjectData.OutputFilename) + // + //// Calculate asset directory + //assetDir := getPackageAssetsDirectory() + // + //// Copy default icon if one doesn't exist + //baseBuildDirectory, err := getBuildBaseDirectory(options) + //if err != nil { + // return err + //} + //iconFile := filepath.Join(baseBuildDirectory, "icon.png") + //if !fs.FileExists(iconFile) { + // err = fs.CopyFile(defaultIconPath(), iconFile) + // if err != nil { + // return err + // } + //} + // + //// Copy Icon + //targetIcon := filepath.Join(buildDirectory, name+".png") + //err = fs.CopyFile(iconFile, targetIcon) + //if err != nil { + // return err + //} + // + //// Copy app.desktop + //dotDesktopFile := filepath.Join(baseBuildDirectory, "linux", name+".desktop") + //if !fs.FileExists(dotDesktopFile) { + // bytes, err := ioutil.ReadFile(filepath.Join(assetDir, "app.desktop")) + // if err != nil { + // return err + // } + // appDesktop := string(bytes) + // appDesktop = strings.ReplaceAll(appDesktop, `{{.Name}}`, name) + // err = ioutil.WriteFile(dotDesktopFile, []byte(appDesktop), 0644) + // if err != nil { + // return err + // } + //} + // + //// Copy AppRun file + //// targetFilename = filepath.Join(buildDirectory, "AppRun") + //// if !fs.FileExists(targetFilename) { + //// bytes, err := ioutil.ReadFile(filepath.Join(assetDir, "AppRun")) + //// if err != nil { + //// return err + //// } + //// appRun := string(bytes) + //// appRun = strings.ReplaceAll(appRun, `{{.OutputFilename}}`, name) + // + //// err = ioutil.WriteFile(targetFilename, []byte(appRun), 0644) + //// if err != nil { + //// return err + //// } + //// } + // + //// Copy Binary + //sourceFile := filepath.Join(options.ProjectData.Path, options.ProjectData.OutputFilename) + //targetFile := filepath.Join(buildDirectory, options.ProjectData.OutputFilename) + //err = fs.CopyFile(sourceFile, targetFile) + //if err != nil { + // return err + //} + //err = os.Chmod(targetFile, 0777) + //if err != nil { + // return err + //} + // + ///** Pack App **/ + // + //// Make file executable + //// Set environment variable: OUTPUT=outputfilename + //command := shell.NewCommand("linuxdeploy-x86_64.AppImage") + //command.Dir(buildDirectory) + // + //argslice := slicer.String() + //argslice.Add("--appdir", "AppDir") + //argslice.Add("-d", filepath.Join("..", name+".desktop")) + //argslice.Add("-i", name+".png") + //argslice.Add("-e", name) + //argslice.Add("--output", "appimage") + //command.AddArgs(argslice.AsSlice()) + // + //command.Env("OUTPUT", name+".AppImage") + // + //err = command.Run() + //if err != nil { + // println(command.Stdout()) + // println(command.Stderr()) + // return err + //} + // + //// Copy app to project dir + // + //println(buildDirectory) + + return nil +} diff --git a/v2/pkg/commands/build/packager_windows.go b/v2/pkg/commands/build/packager_windows.go new file mode 100644 index 000000000..85b395568 --- /dev/null +++ b/v2/pkg/commands/build/packager_windows.go @@ -0,0 +1,129 @@ +package build + +import ( + "fmt" + "github.com/leaanthony/winicon" + "github.com/tc-hib/winres" + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/pkg/buildassets" + "os" + "path/filepath" +) + +func packageApplication(options *Options) error { + // Generate icon + var err error + err = generateIcoFile(options) + if err != nil { + return err + } + + // Ensure Manifest is present + err = generateManifest(options) + if err != nil { + return err + } + + // Create syso file + err = compileResources(options) + if err != nil { + return err + } + + return nil +} + +func generateManifest(options *Options) error { + filename := options.ProjectData.Name + ".exe.manifest" + manifestFile := filepath.Join(options.ProjectData.Path, "build", "windows", filename) + if !fs.FileExists(manifestFile) { + return buildassets.RegenerateManifest(manifestFile) + } + return nil +} + +func generateIcoFile(options *Options) error { + // Check ico file exists already + icoFile := filepath.Join(options.ProjectData.Path, "build", "windows", "icon.ico") + if !fs.FileExists(icoFile) { + // Check icon exists + appicon := filepath.Join(options.ProjectData.Path, "build", "appicon.png") + if !fs.FileExists(appicon) { + return fmt.Errorf("application icon missing: %s", appicon) + } + // Load icon + input, err := os.Open(appicon) + if err != nil { + return err + } + output, err := os.OpenFile(icoFile, os.O_CREATE, 0644) + if err != nil { + return err + } + err = winicon.GenerateIcon(input, output, []int{256, 128, 64, 48, 32, 16}) + if err != nil { + return err + } + } + return nil +} + +func compileResources(options *Options) error { + + currentDir, err := os.Getwd() + if err != nil { + return err + } + defer func() { + os.Chdir(currentDir) + }() + windowsDir := filepath.Join(options.ProjectData.Path, "build", "windows") + err = os.Chdir(windowsDir) + if err != nil { + return err + } + rs := winres.ResourceSet{} + icon := filepath.Join(windowsDir, "icon.ico") + iconFile, err := os.Open(icon) + if err != nil { + return err + } + defer iconFile.Close() + ico, err := winres.LoadICO(iconFile) + if err != nil { + return err + } + err = rs.SetIcon(winres.RT_ICON, ico) + if err != nil { + return err + } + + ManifestFilename := options.ProjectData.Name + ".exe.manifest" + manifestData, err := os.ReadFile(ManifestFilename) + xmlData, err := winres.AppManifestFromXML(manifestData) + if err != nil { + return err + } + rs.SetManifest(xmlData) + + targetFile := filepath.Join(options.ProjectData.Path, options.ProjectData.Name+"-res.syso") + fout, err := os.Create(targetFile) + if err != nil { + return err + } + defer fout.Close() + + archs := map[string]winres.Arch{ + "amd64": winres.ArchAMD64, + } + targetArch, supported := archs[options.Arch] + if !supported { + return fmt.Errorf("arch '%s' not supported", options.Arch) + } + + err = rs.WriteObject(fout, targetArch) + if err != nil { + return err + } + return nil +} diff --git a/v2/pkg/commands/build/server-assetdb.go.template b/v2/pkg/commands/build/server-assetdb.go.template new file mode 100644 index 000000000..f4720fee4 --- /dev/null +++ b/v2/pkg/commands/build/server-assetdb.go.template @@ -0,0 +1,11 @@ +package webserver + +// This sets up the assets that are defined in `webassets.go` +func init() { + html = []byte{$HTMLBYTES$} + js = []byte{$JSBYTES$} + css = []byte{$CSSBYTES$} + wailsjs = []byte{$WAILSJSBYTES$} + jsurl = []byte{$JSURLBYTES$} + cssurl = []byte{$CSSURLBYTES$} +} \ No newline at end of file diff --git a/v2/pkg/commands/build/server.go b/v2/pkg/commands/build/server.go new file mode 100644 index 000000000..fd37aebf8 --- /dev/null +++ b/v2/pkg/commands/build/server.go @@ -0,0 +1,113 @@ +package build + +import ( + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/wailsapp/wails/v2/internal/fs" + "github.com/wailsapp/wails/v2/internal/html" +) + +// ServerBuilder builds applications as a server +type ServerBuilder struct { + *BaseBuilder +} + +func newServerBuilder(options *Options) *ServerBuilder { + result := &ServerBuilder{ + BaseBuilder: NewBaseBuilder(options), + } + return result +} + +// BuildAssets builds the assets for the desktop application +func (s *ServerBuilder) BuildAssets(_ *Options) error { + var err error + + assets, err := s.BaseBuilder.ExtractAssets() + if err != nil { + return err + } + + // Build embedded assets (HTML/JS/CSS/etc) + err = s.BuildBaseAssets(assets) + if err != nil { + return err + } + + return nil +} + +// PostCompilation is called after the compilation step, if successful +func (s *ServerBuilder) PostCompilation(_ *Options) error { + return nil +} + +// BuildBaseAssets builds the base assets +func (s *ServerBuilder) BuildBaseAssets(assets *html.AssetBundle) error { + db, err := assets.ConvertToAssetDB() + if err != nil { + return err + } + + // Fetch, update, and reinject index.html + index, err := db.Read("index.html") + if err != nil { + return fmt.Errorf(`failed to locate "index.html"`) + } + splits := strings.Split(string(index), "") + if len(splits) != 2 { + return fmt.Errorf("unable to locate a tag in your frontend/index.html") + } + injectScript := `` + result := []string{} + result = append(result, splits[0]) + result = append(result, injectScript) + result = append(result, "") + result = append(result, splits[1]) + + db.Remove("index.html") // Remove the non-prefixed index.html + db.AddAsset("/index.html", []byte(strings.Join(result, ""))) + + // Add wails.js + wailsjsPath := fs.RelativePath("../../../internal/runtime/assets/server.js") + if rawData, err := ioutil.ReadFile(wailsjsPath); err == nil { + db.AddAsset("/wails.js", rawData) + } + + targetFile := fs.RelativePath("../../../internal/webserver/webassetsdb.go") + s.addFileToDelete(targetFile) + f, err := os.Create(targetFile) + if err != nil { + return err + } + + _, err = f.WriteString(db.Serialize("db", "webserver")) + if err != nil { + // Ignore error - we already have one! + _ = f.Close() + return err + } + return f.Close() +} + +// BuildRuntime builds the javascript runtime used by the HTML client to connect to the websocket +func (s *ServerBuilder) BuildRuntime(options *Options) error { + + sourceDir := fs.RelativePath("../../../internal/runtime/js") + if err := s.NpmInstall(sourceDir, options.Verbosity == VERBOSE); err != nil { + return err + } + + options.Logger.Print(" - Embedding Runtime...") + envvars := []string{"WAILSPLATFORM=" + options.Platform} + var err error + if err = s.NpmRunWithEnvironment(sourceDir, "build:server", false, envvars); err != nil { + return err + } + + options.Logger.Println("done.") + return nil +} diff --git a/v2/pkg/git/git.go b/v2/pkg/git/git.go new file mode 100644 index 000000000..844fd452f --- /dev/null +++ b/v2/pkg/git/git.go @@ -0,0 +1,38 @@ +package git + +import ( + "runtime" + + "github.com/wailsapp/wails/v2/internal/shell" +) + +func gitcommand() string { + gitcommand := "git" + if runtime.GOOS == "windows" { + gitcommand = "git.exe" + } + + return gitcommand +} + +// IsInstalled returns true if git is installed for the given platform +func IsInstalled() bool { + return shell.CommandExists(gitcommand()) +} + +// Email tries to retrieve the +func Email() (string, error) { + stdout, _, err := shell.RunCommand(".", gitcommand(), "config", "user.email") + return stdout, err +} + +// Name tries to retrieve the +func Name() (string, error) { + stdout, _, err := shell.RunCommand(".", gitcommand(), "config", "user.name") + return stdout, err +} + +func InitRepo(projectDir string) error { + _, _, err := shell.RunCommand(projectDir, gitcommand(), "init") + return err +} diff --git a/v2/pkg/logger/default.go b/v2/pkg/logger/default.go new file mode 100644 index 000000000..ac83d4f2f --- /dev/null +++ b/v2/pkg/logger/default.go @@ -0,0 +1,49 @@ +package logger + +import ( + "os" +) + +// DefaultLogger is a utility to log messages to a number of destinations +type DefaultLogger struct{} + +// NewDefaultLogger creates a new Logger. +func NewDefaultLogger() Logger { + return &DefaultLogger{} +} + +// Print works like Sprintf. +func (l *DefaultLogger) Print(message string) { + println(message) +} + +// Trace level logging. Works like Sprintf. +func (l *DefaultLogger) Trace(message string) { + println("TRA | " + message) +} + +// Debug level logging. Works like Sprintf. +func (l *DefaultLogger) Debug(message string) { + println("DEB | " + message) +} + +// Info level logging. Works like Sprintf. +func (l *DefaultLogger) Info(message string) { + println("INF | " + message) +} + +// Warning level logging. Works like Sprintf. +func (l *DefaultLogger) Warning(message string) { + println("WAR | " + message) +} + +// Error level logging. Works like Sprintf. +func (l *DefaultLogger) Error(message string) { + println("ERR | " + message) +} + +// Fatal level logging. Works like Sprintf. +func (l *DefaultLogger) Fatal(message string) { + println("FAT | " + message) + os.Exit(1) +} diff --git a/v2/pkg/logger/filelogger.go b/v2/pkg/logger/filelogger.go new file mode 100644 index 000000000..5cf68fc1a --- /dev/null +++ b/v2/pkg/logger/filelogger.go @@ -0,0 +1,66 @@ +package logger + +import ( + "log" + "os" +) + +// FileLogger is a utility to log messages to a number of destinations +type FileLogger struct { + filename string +} + +// NewFileLogger creates a new Logger. +func NewFileLogger(filename string) Logger { + return &FileLogger{ + filename: filename, + } +} + +// Print works like Sprintf. +func (l *FileLogger) Print(message string) { + f, err := os.OpenFile(l.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatal(err) + } + if _, err := f.WriteString(message); err != nil { + f.Close() + log.Fatal(err) + } + f.Close() +} + +func (l *FileLogger) Println(message string) { + l.Print(message + "\n") +} + +// Trace level logging. Works like Sprintf. +func (l *FileLogger) Trace(message string) { + l.Println("TRACE | " + message) +} + +// Debug level logging. Works like Sprintf. +func (l *FileLogger) Debug(message string) { + l.Println("DEBUG | " + message) +} + +// Info level logging. Works like Sprintf. +func (l *FileLogger) Info(message string) { + l.Println("INFO | " + message) +} + +// Warning level logging. Works like Sprintf. +func (l *FileLogger) Warning(message string) { + l.Println("WARN | " + message) +} + +// Error level logging. Works like Sprintf. +func (l *FileLogger) Error(message string) { + l.Println("ERROR | " + message) +} + +// Fatal level logging. Works like Sprintf. +func (l *FileLogger) Fatal(message string) { + l.Println("FATAL | " + message) + os.Exit(1) +} diff --git a/v2/pkg/logger/logger.go b/v2/pkg/logger/logger.go new file mode 100644 index 000000000..abc288265 --- /dev/null +++ b/v2/pkg/logger/logger.go @@ -0,0 +1,54 @@ +package logger + +import ( + "fmt" + "strings" +) + +// LogLevel is an unsigned 8bit int +type LogLevel uint8 + +const ( + // TRACE level + TRACE LogLevel = 1 + + // DEBUG level logging + DEBUG LogLevel = 2 + + // INFO level logging + INFO LogLevel = 3 + + // WARNING level logging + WARNING LogLevel = 4 + + // ERROR level logging + ERROR LogLevel = 5 +) + +var logLevelMap = map[string]LogLevel{ + "trace": TRACE, + "debug": DEBUG, + "info": INFO, + "warning": WARNING, + "error": ERROR, +} + +func StringToLogLevel(input string) (LogLevel, error) { + result, ok := logLevelMap[strings.ToLower(input)] + if !ok { + return ERROR, fmt.Errorf("invalid log level: %s", input) + } + return result, nil +} + +// Logger specifies the methods required to attach +// a logger to a Wails application +type Logger interface { + Print(message string) + Trace(message string) + Debug(message string) + Info(message string) + Warning(message string) + Error(message string) + Fatal(message string) +} diff --git a/v2/pkg/mac/login_darwin.go b/v2/pkg/mac/login_darwin.go new file mode 100644 index 000000000..2ff49be83 --- /dev/null +++ b/v2/pkg/mac/login_darwin.go @@ -0,0 +1,60 @@ +// Package mac provides MacOS related utility functions for Wails applications +package mac + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/leaanthony/slicer" + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/internal/shell" +) + +// StartAtLogin will either add or remove this application to/from the login +// items, depending on the given boolean flag. The limitation is that the +// currently running app must be in an app bundle. +func StartAtLogin(enabled bool) error { + exe, err := os.Executable() + if err != nil { + return errors.Wrap(err, "Error running os.Executable:") + } + binName := filepath.Base(exe) + if !strings.HasSuffix(exe, "/Contents/MacOS/"+binName) { + return fmt.Errorf("app needs to be running as package.app file to start at login") + } + appPath := strings.TrimSuffix(exe, "/Contents/MacOS/"+binName) + var command string + if enabled { + command = fmt.Sprintf("tell application \"System Events\" to make login item at end with properties {name: \"%s\",path:\"%s\", hidden:false}", binName, appPath) + } else { + command = fmt.Sprintf("tell application \"System Events\" to delete login item \"%s\"", binName) + } + _, stde, err := shell.RunCommand("/tmp", "osascript", "-e", command) + if err != nil { + errors.Wrap(err, stde) + } + return nil +} + +// StartsAtLogin will indicate if this application is in the login +// items. The limitation is that the currently running app must be +// in an app bundle. +func StartsAtLogin() (bool, error) { + exe, err := os.Executable() + if err != nil { + return false, err + } + binName := filepath.Base(exe) + if !strings.HasSuffix(exe, "/Contents/MacOS/"+binName) { + return false, fmt.Errorf("app needs to be running as package.app file to start at login") + } + results, stde, err := shell.RunCommand("/tmp", "osascript", "-e", `tell application "System Events" to get the name of every login item`) + if err != nil { + return false, errors.Wrap(err, stde) + } + results = strings.TrimSpace(results) + startupApps := slicer.String(strings.Split(results, ", ")) + return startupApps.Contains(binName), nil +} diff --git a/v2/pkg/mac/notification_darwin.go b/v2/pkg/mac/notification_darwin.go new file mode 100644 index 000000000..a06ecb53a --- /dev/null +++ b/v2/pkg/mac/notification_darwin.go @@ -0,0 +1,30 @@ +// Package mac provides MacOS related utility functions for Wails applications +package mac + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/internal/shell" +) + +// StartAtLogin will either add or remove this application to/from the login +// items, depending on the given boolean flag. The limitation is that the +// currently running app must be in an app bundle. +func ShowNotification(title string, subtitle string, message string, sound string) error { + command := fmt.Sprintf("display notification \"%s\"", message) + if len(title) > 0 { + command += fmt.Sprintf(" with title \"%s\"", title) + } + if len(subtitle) > 0 { + command += fmt.Sprintf(" subtitle \"%s\"", subtitle) + } + if len(sound) > 0 { + command += fmt.Sprintf(" sound name \"%s\"", sound) + } + _, stde, err := shell.RunCommand("/tmp", "osascript", "-e", command) + if err != nil { + errors.Wrap(err, stde) + } + return nil +} diff --git a/v2/pkg/mac/notification_darwin_test.go b/v2/pkg/mac/notification_darwin_test.go new file mode 100644 index 000000000..7d14c00e5 --- /dev/null +++ b/v2/pkg/mac/notification_darwin_test.go @@ -0,0 +1,34 @@ +package mac + +import "testing" + +func TestShowNotification(t *testing.T) { + type args struct { + title string + subtitle string + message string + sound string + } + tests := []struct { + name string + title string + subtitle string + message string + sound string + wantErr bool + }{ + {"No message", "", "", "", "", false}, + {"Title only", "I am a Title", "", "", "", false}, + {"SubTitle only", "", "I am a subtitle", "", "", false}, + {"Message only", "", "", "I am a message!", "", false}, + {"Sound only", "", "", "", "submarine.aiff", false}, + {"Full", "Title", "Subtitle", "This is a long message to show that text gets wrapped in a notification", "submarine.aiff", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := ShowNotification(tt.title, tt.subtitle, tt.message, tt.sound); (err != nil) != tt.wantErr { + t.Errorf("ShowNotification() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/v2/pkg/menu/README.md b/v2/pkg/menu/README.md new file mode 100644 index 000000000..7c66a1051 --- /dev/null +++ b/v2/pkg/menu/README.md @@ -0,0 +1,10 @@ +# Menus + +Menu support is heavily inspired by Electron's approach. + +## Features + + * Supports Text, Checkbox, Radio, Submenu and Separator + * Radio groups are defined as any number of adjacent radio items + * UTF-8 menu labels + * UTF-8 menu IDs \ No newline at end of file diff --git a/v2/pkg/menu/callback.go b/v2/pkg/menu/callback.go new file mode 100644 index 000000000..fe6160361 --- /dev/null +++ b/v2/pkg/menu/callback.go @@ -0,0 +1,8 @@ +package menu + +type CallbackData struct { + MenuItem *MenuItem + //ContextData string +} + +type Callback func(*CallbackData) diff --git a/v2/pkg/menu/colours/colours.go b/v2/pkg/menu/colours/colours.go new file mode 100644 index 000000000..c0f1da05c --- /dev/null +++ b/v2/pkg/menu/colours/colours.go @@ -0,0 +1,66 @@ +package main + +import ( + "bytes" + _ "embed" + "encoding/json" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" + "text/template" +) + +type Rgb struct { + R uint8 `json:"r"` + G uint8 `json:"g"` + B uint8 `json:"b"` +} + +type Hsl struct { + H float64 `json:"h"` + S float64 `json:"s"` + L float64 `json:"l"` +} + +type InputCol struct { + Colorid uint8 `json:"colorId"` + Hexstring string `json:"hexString"` + Rgb Rgb `json:"rgb"` + Hsl Hsl `json:"hsl"` + Name string `json:"name"` +} + +//go:embed gen.tmpl +var Template string + +func main() { + + var Cols []InputCol + + resp, err := http.Get("https://jonasjacek.github.io/colors/data.json") + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + } + err = json.Unmarshal(data, &Cols) + if err != nil { + log.Fatal(err) + } + + t, err := template.New("cols").Parse(Template) + if err != nil { + log.Fatal(err) + } + var buffer bytes.Buffer + err = t.Execute(&buffer, Cols) + if err != nil { + log.Fatal(err) + } + os.WriteFile(filepath.Join("..", "cols.go"), buffer.Bytes(), 0755) +} diff --git a/v2/pkg/menu/colours/gen.tmpl b/v2/pkg/menu/colours/gen.tmpl new file mode 100644 index 000000000..bd5d1ff3c --- /dev/null +++ b/v2/pkg/menu/colours/gen.tmpl @@ -0,0 +1,29 @@ +package menu + +type Rgb struct { + R uint8 `json:"r"` + G uint8 `json:"g"` + B uint8 `json:"b"` +} + +type Hsl struct { + H float64 `json:"h"` + S float64 `json:"s"` + L float64 `json:"l"` +} + +type Col struct { + Hex string `json:"hex"` + Rgb Rgb `json:"rgb"` + Hsl Hsl `json:"hsl"` + Name string `json:"name"` +} + +var Cols = []*Col{ {{range $col := .}} + { + Hex: "{{.Hexstring}}", + Rgb: Rgb{ {{.Rgb.R}}, {{.Rgb.G}}, {{.Rgb.B}} }, + Hsl: Hsl{ {{.Hsl.H}}, {{.Hsl.S}}, {{.Hsl.L}} }, + Name: "{{.Name}}", + },{{end}} +} diff --git a/v2/pkg/menu/cols.go b/v2/pkg/menu/cols.go new file mode 100755 index 000000000..738e4baaf --- /dev/null +++ b/v2/pkg/menu/cols.go @@ -0,0 +1,1559 @@ +package menu + +type Rgb struct { + R uint8 `json:"r"` + G uint8 `json:"g"` + B uint8 `json:"b"` +} + +type Hsl struct { + H float64 `json:"h"` + S float64 `json:"s"` + L float64 `json:"l"` +} + +type Col struct { + Hex string `json:"hex"` + Rgb Rgb `json:"rgb"` + Hsl Hsl `json:"hsl"` + Name string `json:"name"` +} + +var Cols = []*Col{ + { + Hex: "#000000", + Rgb: Rgb{0, 0, 0}, + Hsl: Hsl{0, 0, 0}, + Name: "Black", + }, + { + Hex: "#800000", + Rgb: Rgb{128, 0, 0}, + Hsl: Hsl{0, 100, 25}, + Name: "Maroon", + }, + { + Hex: "#008000", + Rgb: Rgb{0, 128, 0}, + Hsl: Hsl{120, 100, 25}, + Name: "Green", + }, + { + Hex: "#808000", + Rgb: Rgb{128, 128, 0}, + Hsl: Hsl{60, 100, 25}, + Name: "Olive", + }, + { + Hex: "#000080", + Rgb: Rgb{0, 0, 128}, + Hsl: Hsl{240, 100, 25}, + Name: "Navy", + }, + { + Hex: "#800080", + Rgb: Rgb{128, 0, 128}, + Hsl: Hsl{300, 100, 25}, + Name: "Purple", + }, + { + Hex: "#008080", + Rgb: Rgb{0, 128, 128}, + Hsl: Hsl{180, 100, 25}, + Name: "Teal", + }, + { + Hex: "#c0c0c0", + Rgb: Rgb{192, 192, 192}, + Hsl: Hsl{0, 0, 75}, + Name: "Silver", + }, + { + Hex: "#808080", + Rgb: Rgb{128, 128, 128}, + Hsl: Hsl{0, 0, 50}, + Name: "Grey", + }, + { + Hex: "#ff0000", + Rgb: Rgb{255, 0, 0}, + Hsl: Hsl{0, 100, 50}, + Name: "Red", + }, + { + Hex: "#00ff00", + Rgb: Rgb{0, 255, 0}, + Hsl: Hsl{120, 100, 50}, + Name: "Lime", + }, + { + Hex: "#ffff00", + Rgb: Rgb{255, 255, 0}, + Hsl: Hsl{60, 100, 50}, + Name: "Yellow", + }, + { + Hex: "#0000ff", + Rgb: Rgb{0, 0, 255}, + Hsl: Hsl{240, 100, 50}, + Name: "Blue", + }, + { + Hex: "#ff00ff", + Rgb: Rgb{255, 0, 255}, + Hsl: Hsl{300, 100, 50}, + Name: "Fuchsia", + }, + { + Hex: "#00ffff", + Rgb: Rgb{0, 255, 255}, + Hsl: Hsl{180, 100, 50}, + Name: "Aqua", + }, + { + Hex: "#ffffff", + Rgb: Rgb{255, 255, 255}, + Hsl: Hsl{0, 0, 100}, + Name: "White", + }, + { + Hex: "#000000", + Rgb: Rgb{0, 0, 0}, + Hsl: Hsl{0, 0, 0}, + Name: "Grey0", + }, + { + Hex: "#00005f", + Rgb: Rgb{0, 0, 95}, + Hsl: Hsl{240, 100, 18}, + Name: "NavyBlue", + }, + { + Hex: "#000087", + Rgb: Rgb{0, 0, 135}, + Hsl: Hsl{240, 100, 26}, + Name: "DarkBlue", + }, + { + Hex: "#0000af", + Rgb: Rgb{0, 0, 175}, + Hsl: Hsl{240, 100, 34}, + Name: "Blue3", + }, + { + Hex: "#0000d7", + Rgb: Rgb{0, 0, 215}, + Hsl: Hsl{240, 100, 42}, + Name: "Blue3", + }, + { + Hex: "#0000ff", + Rgb: Rgb{0, 0, 255}, + Hsl: Hsl{240, 100, 50}, + Name: "Blue1", + }, + { + Hex: "#005f00", + Rgb: Rgb{0, 95, 0}, + Hsl: Hsl{120, 100, 18}, + Name: "DarkGreen", + }, + { + Hex: "#005f5f", + Rgb: Rgb{0, 95, 95}, + Hsl: Hsl{180, 100, 18}, + Name: "DeepSkyBlue4", + }, + { + Hex: "#005f87", + Rgb: Rgb{0, 95, 135}, + Hsl: Hsl{197.777777777778, 100, 26}, + Name: "DeepSkyBlue4", + }, + { + Hex: "#005faf", + Rgb: Rgb{0, 95, 175}, + Hsl: Hsl{207.428571428571, 100, 34}, + Name: "DeepSkyBlue4", + }, + { + Hex: "#005fd7", + Rgb: Rgb{0, 95, 215}, + Hsl: Hsl{213.488372093023, 100, 42}, + Name: "DodgerBlue3", + }, + { + Hex: "#005fff", + Rgb: Rgb{0, 95, 255}, + Hsl: Hsl{217.647058823529, 100, 50}, + Name: "DodgerBlue2", + }, + { + Hex: "#008700", + Rgb: Rgb{0, 135, 0}, + Hsl: Hsl{120, 100, 26}, + Name: "Green4", + }, + { + Hex: "#00875f", + Rgb: Rgb{0, 135, 95}, + Hsl: Hsl{162.222222222222, 100, 26}, + Name: "SpringGreen4", + }, + { + Hex: "#008787", + Rgb: Rgb{0, 135, 135}, + Hsl: Hsl{180, 100, 26}, + Name: "Turquoise4", + }, + { + Hex: "#0087af", + Rgb: Rgb{0, 135, 175}, + Hsl: Hsl{193.714285714286, 100, 34}, + Name: "DeepSkyBlue3", + }, + { + Hex: "#0087d7", + Rgb: Rgb{0, 135, 215}, + Hsl: Hsl{202.325581395349, 100, 42}, + Name: "DeepSkyBlue3", + }, + { + Hex: "#0087ff", + Rgb: Rgb{0, 135, 255}, + Hsl: Hsl{208.235294117647, 100, 50}, + Name: "DodgerBlue1", + }, + { + Hex: "#00af00", + Rgb: Rgb{0, 175, 0}, + Hsl: Hsl{120, 100, 34}, + Name: "Green3", + }, + { + Hex: "#00af5f", + Rgb: Rgb{0, 175, 95}, + Hsl: Hsl{152.571428571429, 100, 34}, + Name: "SpringGreen3", + }, + { + Hex: "#00af87", + Rgb: Rgb{0, 175, 135}, + Hsl: Hsl{166.285714285714, 100, 34}, + Name: "DarkCyan", + }, + { + Hex: "#00afaf", + Rgb: Rgb{0, 175, 175}, + Hsl: Hsl{180, 100, 34}, + Name: "LightSeaGreen", + }, + { + Hex: "#00afd7", + Rgb: Rgb{0, 175, 215}, + Hsl: Hsl{191.162790697674, 100, 42}, + Name: "DeepSkyBlue2", + }, + { + Hex: "#00afff", + Rgb: Rgb{0, 175, 255}, + Hsl: Hsl{198.823529411765, 100, 50}, + Name: "DeepSkyBlue1", + }, + { + Hex: "#00d700", + Rgb: Rgb{0, 215, 0}, + Hsl: Hsl{120, 100, 42}, + Name: "Green3", + }, + { + Hex: "#00d75f", + Rgb: Rgb{0, 215, 95}, + Hsl: Hsl{146.511627906977, 100, 42}, + Name: "SpringGreen3", + }, + { + Hex: "#00d787", + Rgb: Rgb{0, 215, 135}, + Hsl: Hsl{157.674418604651, 100, 42}, + Name: "SpringGreen2", + }, + { + Hex: "#00d7af", + Rgb: Rgb{0, 215, 175}, + Hsl: Hsl{168.837209302326, 100, 42}, + Name: "Cyan3", + }, + { + Hex: "#00d7d7", + Rgb: Rgb{0, 215, 215}, + Hsl: Hsl{180, 100, 42}, + Name: "DarkTurquoise", + }, + { + Hex: "#00d7ff", + Rgb: Rgb{0, 215, 255}, + Hsl: Hsl{189.411764705882, 100, 50}, + Name: "Turquoise2", + }, + { + Hex: "#00ff00", + Rgb: Rgb{0, 255, 0}, + Hsl: Hsl{120, 100, 50}, + Name: "Green1", + }, + { + Hex: "#00ff5f", + Rgb: Rgb{0, 255, 95}, + Hsl: Hsl{142.352941176471, 100, 50}, + Name: "SpringGreen2", + }, + { + Hex: "#00ff87", + Rgb: Rgb{0, 255, 135}, + Hsl: Hsl{151.764705882353, 100, 50}, + Name: "SpringGreen1", + }, + { + Hex: "#00ffaf", + Rgb: Rgb{0, 255, 175}, + Hsl: Hsl{161.176470588235, 100, 50}, + Name: "MediumSpringGreen", + }, + { + Hex: "#00ffd7", + Rgb: Rgb{0, 255, 215}, + Hsl: Hsl{170.588235294118, 100, 50}, + Name: "Cyan2", + }, + { + Hex: "#00ffff", + Rgb: Rgb{0, 255, 255}, + Hsl: Hsl{180, 100, 50}, + Name: "Cyan1", + }, + { + Hex: "#5f0000", + Rgb: Rgb{95, 0, 0}, + Hsl: Hsl{0, 100, 18}, + Name: "DarkRed", + }, + { + Hex: "#5f005f", + Rgb: Rgb{95, 0, 95}, + Hsl: Hsl{300, 100, 18}, + Name: "DeepPink4", + }, + { + Hex: "#5f0087", + Rgb: Rgb{95, 0, 135}, + Hsl: Hsl{282.222222222222, 100, 26}, + Name: "Purple4", + }, + { + Hex: "#5f00af", + Rgb: Rgb{95, 0, 175}, + Hsl: Hsl{272.571428571429, 100, 34}, + Name: "Purple4", + }, + { + Hex: "#5f00d7", + Rgb: Rgb{95, 0, 215}, + Hsl: Hsl{266.511627906977, 100, 42}, + Name: "Purple3", + }, + { + Hex: "#5f00ff", + Rgb: Rgb{95, 0, 255}, + Hsl: Hsl{262.352941176471, 100, 50}, + Name: "BlueViolet", + }, + { + Hex: "#5f5f00", + Rgb: Rgb{95, 95, 0}, + Hsl: Hsl{60, 100, 18}, + Name: "Orange4", + }, + { + Hex: "#5f5f5f", + Rgb: Rgb{95, 95, 95}, + Hsl: Hsl{0, 0, 37}, + Name: "Grey37", + }, + { + Hex: "#5f5f87", + Rgb: Rgb{95, 95, 135}, + Hsl: Hsl{240, 17, 45}, + Name: "MediumPurple4", + }, + { + Hex: "#5f5faf", + Rgb: Rgb{95, 95, 175}, + Hsl: Hsl{240, 33, 52}, + Name: "SlateBlue3", + }, + { + Hex: "#5f5fd7", + Rgb: Rgb{95, 95, 215}, + Hsl: Hsl{240, 60, 60}, + Name: "SlateBlue3", + }, + { + Hex: "#5f5fff", + Rgb: Rgb{95, 95, 255}, + Hsl: Hsl{240, 100, 68}, + Name: "RoyalBlue1", + }, + { + Hex: "#5f8700", + Rgb: Rgb{95, 135, 0}, + Hsl: Hsl{77.7777777777778, 100, 26}, + Name: "Chartreuse4", + }, + { + Hex: "#5f875f", + Rgb: Rgb{95, 135, 95}, + Hsl: Hsl{120, 17, 45}, + Name: "DarkSeaGreen4", + }, + { + Hex: "#5f8787", + Rgb: Rgb{95, 135, 135}, + Hsl: Hsl{180, 17, 45}, + Name: "PaleTurquoise4", + }, + { + Hex: "#5f87af", + Rgb: Rgb{95, 135, 175}, + Hsl: Hsl{210, 33, 52}, + Name: "SteelBlue", + }, + { + Hex: "#5f87d7", + Rgb: Rgb{95, 135, 215}, + Hsl: Hsl{220, 60, 60}, + Name: "SteelBlue3", + }, + { + Hex: "#5f87ff", + Rgb: Rgb{95, 135, 255}, + Hsl: Hsl{225, 100, 68}, + Name: "CornflowerBlue", + }, + { + Hex: "#5faf00", + Rgb: Rgb{95, 175, 0}, + Hsl: Hsl{87.4285714285714, 100, 34}, + Name: "Chartreuse3", + }, + { + Hex: "#5faf5f", + Rgb: Rgb{95, 175, 95}, + Hsl: Hsl{120, 33, 52}, + Name: "DarkSeaGreen4", + }, + { + Hex: "#5faf87", + Rgb: Rgb{95, 175, 135}, + Hsl: Hsl{150, 33, 52}, + Name: "CadetBlue", + }, + { + Hex: "#5fafaf", + Rgb: Rgb{95, 175, 175}, + Hsl: Hsl{180, 33, 52}, + Name: "CadetBlue", + }, + { + Hex: "#5fafd7", + Rgb: Rgb{95, 175, 215}, + Hsl: Hsl{200, 60, 60}, + Name: "SkyBlue3", + }, + { + Hex: "#5fafff", + Rgb: Rgb{95, 175, 255}, + Hsl: Hsl{210, 100, 68}, + Name: "SteelBlue1", + }, + { + Hex: "#5fd700", + Rgb: Rgb{95, 215, 0}, + Hsl: Hsl{93.4883720930233, 100, 42}, + Name: "Chartreuse3", + }, + { + Hex: "#5fd75f", + Rgb: Rgb{95, 215, 95}, + Hsl: Hsl{120, 60, 60}, + Name: "PaleGreen3", + }, + { + Hex: "#5fd787", + Rgb: Rgb{95, 215, 135}, + Hsl: Hsl{140, 60, 60}, + Name: "SeaGreen3", + }, + { + Hex: "#5fd7af", + Rgb: Rgb{95, 215, 175}, + Hsl: Hsl{160, 60, 60}, + Name: "Aquamarine3", + }, + { + Hex: "#5fd7d7", + Rgb: Rgb{95, 215, 215}, + Hsl: Hsl{180, 60, 60}, + Name: "MediumTurquoise", + }, + { + Hex: "#5fd7ff", + Rgb: Rgb{95, 215, 255}, + Hsl: Hsl{195, 100, 68}, + Name: "SteelBlue1", + }, + { + Hex: "#5fff00", + Rgb: Rgb{95, 255, 0}, + Hsl: Hsl{97.6470588235294, 100, 50}, + Name: "Chartreuse2", + }, + { + Hex: "#5fff5f", + Rgb: Rgb{95, 255, 95}, + Hsl: Hsl{120, 100, 68}, + Name: "SeaGreen2", + }, + { + Hex: "#5fff87", + Rgb: Rgb{95, 255, 135}, + Hsl: Hsl{135, 100, 68}, + Name: "SeaGreen1", + }, + { + Hex: "#5fffaf", + Rgb: Rgb{95, 255, 175}, + Hsl: Hsl{150, 100, 68}, + Name: "SeaGreen1", + }, + { + Hex: "#5fffd7", + Rgb: Rgb{95, 255, 215}, + Hsl: Hsl{165, 100, 68}, + Name: "Aquamarine1", + }, + { + Hex: "#5fffff", + Rgb: Rgb{95, 255, 255}, + Hsl: Hsl{180, 100, 68}, + Name: "DarkSlateGray2", + }, + { + Hex: "#870000", + Rgb: Rgb{135, 0, 0}, + Hsl: Hsl{0, 100, 26}, + Name: "DarkRed", + }, + { + Hex: "#87005f", + Rgb: Rgb{135, 0, 95}, + Hsl: Hsl{317.777777777778, 100, 26}, + Name: "DeepPink4", + }, + { + Hex: "#870087", + Rgb: Rgb{135, 0, 135}, + Hsl: Hsl{300, 100, 26}, + Name: "DarkMagenta", + }, + { + Hex: "#8700af", + Rgb: Rgb{135, 0, 175}, + Hsl: Hsl{286.285714285714, 100, 34}, + Name: "DarkMagenta", + }, + { + Hex: "#8700d7", + Rgb: Rgb{135, 0, 215}, + Hsl: Hsl{277.674418604651, 100, 42}, + Name: "DarkViolet", + }, + { + Hex: "#8700ff", + Rgb: Rgb{135, 0, 255}, + Hsl: Hsl{271.764705882353, 100, 50}, + Name: "Purple", + }, + { + Hex: "#875f00", + Rgb: Rgb{135, 95, 0}, + Hsl: Hsl{42.2222222222222, 100, 26}, + Name: "Orange4", + }, + { + Hex: "#875f5f", + Rgb: Rgb{135, 95, 95}, + Hsl: Hsl{0, 17, 45}, + Name: "LightPink4", + }, + { + Hex: "#875f87", + Rgb: Rgb{135, 95, 135}, + Hsl: Hsl{300, 17, 45}, + Name: "Plum4", + }, + { + Hex: "#875faf", + Rgb: Rgb{135, 95, 175}, + Hsl: Hsl{270, 33, 52}, + Name: "MediumPurple3", + }, + { + Hex: "#875fd7", + Rgb: Rgb{135, 95, 215}, + Hsl: Hsl{260, 60, 60}, + Name: "MediumPurple3", + }, + { + Hex: "#875fff", + Rgb: Rgb{135, 95, 255}, + Hsl: Hsl{255, 100, 68}, + Name: "SlateBlue1", + }, + { + Hex: "#878700", + Rgb: Rgb{135, 135, 0}, + Hsl: Hsl{60, 100, 26}, + Name: "Yellow4", + }, + { + Hex: "#87875f", + Rgb: Rgb{135, 135, 95}, + Hsl: Hsl{60, 17, 45}, + Name: "Wheat4", + }, + { + Hex: "#878787", + Rgb: Rgb{135, 135, 135}, + Hsl: Hsl{0, 0, 52}, + Name: "Grey53", + }, + { + Hex: "#8787af", + Rgb: Rgb{135, 135, 175}, + Hsl: Hsl{240, 20, 60}, + Name: "LightSlateGrey", + }, + { + Hex: "#8787d7", + Rgb: Rgb{135, 135, 215}, + Hsl: Hsl{240, 50, 68}, + Name: "MediumPurple", + }, + { + Hex: "#8787ff", + Rgb: Rgb{135, 135, 255}, + Hsl: Hsl{240, 100, 76}, + Name: "LightSlateBlue", + }, + { + Hex: "#87af00", + Rgb: Rgb{135, 175, 0}, + Hsl: Hsl{73.7142857142857, 100, 34}, + Name: "Yellow4", + }, + { + Hex: "#87af5f", + Rgb: Rgb{135, 175, 95}, + Hsl: Hsl{90, 33, 52}, + Name: "DarkOliveGreen3", + }, + { + Hex: "#87af87", + Rgb: Rgb{135, 175, 135}, + Hsl: Hsl{120, 20, 60}, + Name: "DarkSeaGreen", + }, + { + Hex: "#87afaf", + Rgb: Rgb{135, 175, 175}, + Hsl: Hsl{180, 20, 60}, + Name: "LightSkyBlue3", + }, + { + Hex: "#87afd7", + Rgb: Rgb{135, 175, 215}, + Hsl: Hsl{210, 50, 68}, + Name: "LightSkyBlue3", + }, + { + Hex: "#87afff", + Rgb: Rgb{135, 175, 255}, + Hsl: Hsl{220, 100, 76}, + Name: "SkyBlue2", + }, + { + Hex: "#87d700", + Rgb: Rgb{135, 215, 0}, + Hsl: Hsl{82.3255813953488, 100, 42}, + Name: "Chartreuse2", + }, + { + Hex: "#87d75f", + Rgb: Rgb{135, 215, 95}, + Hsl: Hsl{100, 60, 60}, + Name: "DarkOliveGreen3", + }, + { + Hex: "#87d787", + Rgb: Rgb{135, 215, 135}, + Hsl: Hsl{120, 50, 68}, + Name: "PaleGreen3", + }, + { + Hex: "#87d7af", + Rgb: Rgb{135, 215, 175}, + Hsl: Hsl{150, 50, 68}, + Name: "DarkSeaGreen3", + }, + { + Hex: "#87d7d7", + Rgb: Rgb{135, 215, 215}, + Hsl: Hsl{180, 50, 68}, + Name: "DarkSlateGray3", + }, + { + Hex: "#87d7ff", + Rgb: Rgb{135, 215, 255}, + Hsl: Hsl{200, 100, 76}, + Name: "SkyBlue1", + }, + { + Hex: "#87ff00", + Rgb: Rgb{135, 255, 0}, + Hsl: Hsl{88.2352941176471, 100, 50}, + Name: "Chartreuse1", + }, + { + Hex: "#87ff5f", + Rgb: Rgb{135, 255, 95}, + Hsl: Hsl{105, 100, 68}, + Name: "LightGreen", + }, + { + Hex: "#87ff87", + Rgb: Rgb{135, 255, 135}, + Hsl: Hsl{120, 100, 76}, + Name: "LightGreen", + }, + { + Hex: "#87ffaf", + Rgb: Rgb{135, 255, 175}, + Hsl: Hsl{140, 100, 76}, + Name: "PaleGreen1", + }, + { + Hex: "#87ffd7", + Rgb: Rgb{135, 255, 215}, + Hsl: Hsl{160, 100, 76}, + Name: "Aquamarine1", + }, + { + Hex: "#87ffff", + Rgb: Rgb{135, 255, 255}, + Hsl: Hsl{180, 100, 76}, + Name: "DarkSlateGray1", + }, + { + Hex: "#af0000", + Rgb: Rgb{175, 0, 0}, + Hsl: Hsl{0, 100, 34}, + Name: "Red3", + }, + { + Hex: "#af005f", + Rgb: Rgb{175, 0, 95}, + Hsl: Hsl{327.428571428571, 100, 34}, + Name: "DeepPink4", + }, + { + Hex: "#af0087", + Rgb: Rgb{175, 0, 135}, + Hsl: Hsl{313.714285714286, 100, 34}, + Name: "MediumVioletRed", + }, + { + Hex: "#af00af", + Rgb: Rgb{175, 0, 175}, + Hsl: Hsl{300, 100, 34}, + Name: "Magenta3", + }, + { + Hex: "#af00d7", + Rgb: Rgb{175, 0, 215}, + Hsl: Hsl{288.837209302326, 100, 42}, + Name: "DarkViolet", + }, + { + Hex: "#af00ff", + Rgb: Rgb{175, 0, 255}, + Hsl: Hsl{281.176470588235, 100, 50}, + Name: "Purple", + }, + { + Hex: "#af5f00", + Rgb: Rgb{175, 95, 0}, + Hsl: Hsl{32.5714285714286, 100, 34}, + Name: "DarkOrange3", + }, + { + Hex: "#af5f5f", + Rgb: Rgb{175, 95, 95}, + Hsl: Hsl{0, 33, 52}, + Name: "IndianRed", + }, + { + Hex: "#af5f87", + Rgb: Rgb{175, 95, 135}, + Hsl: Hsl{330, 33, 52}, + Name: "HotPink3", + }, + { + Hex: "#af5faf", + Rgb: Rgb{175, 95, 175}, + Hsl: Hsl{300, 33, 52}, + Name: "MediumOrchid3", + }, + { + Hex: "#af5fd7", + Rgb: Rgb{175, 95, 215}, + Hsl: Hsl{280, 60, 60}, + Name: "MediumOrchid", + }, + { + Hex: "#af5fff", + Rgb: Rgb{175, 95, 255}, + Hsl: Hsl{270, 100, 68}, + Name: "MediumPurple2", + }, + { + Hex: "#af8700", + Rgb: Rgb{175, 135, 0}, + Hsl: Hsl{46.2857142857143, 100, 34}, + Name: "DarkGoldenrod", + }, + { + Hex: "#af875f", + Rgb: Rgb{175, 135, 95}, + Hsl: Hsl{30, 33, 52}, + Name: "LightSalmon3", + }, + { + Hex: "#af8787", + Rgb: Rgb{175, 135, 135}, + Hsl: Hsl{0, 20, 60}, + Name: "RosyBrown", + }, + { + Hex: "#af87af", + Rgb: Rgb{175, 135, 175}, + Hsl: Hsl{300, 20, 60}, + Name: "Grey63", + }, + { + Hex: "#af87d7", + Rgb: Rgb{175, 135, 215}, + Hsl: Hsl{270, 50, 68}, + Name: "MediumPurple2", + }, + { + Hex: "#af87ff", + Rgb: Rgb{175, 135, 255}, + Hsl: Hsl{260, 100, 76}, + Name: "MediumPurple1", + }, + { + Hex: "#afaf00", + Rgb: Rgb{175, 175, 0}, + Hsl: Hsl{60, 100, 34}, + Name: "Gold3", + }, + { + Hex: "#afaf5f", + Rgb: Rgb{175, 175, 95}, + Hsl: Hsl{60, 33, 52}, + Name: "DarkKhaki", + }, + { + Hex: "#afaf87", + Rgb: Rgb{175, 175, 135}, + Hsl: Hsl{60, 20, 60}, + Name: "NavajoWhite3", + }, + { + Hex: "#afafaf", + Rgb: Rgb{175, 175, 175}, + Hsl: Hsl{0, 0, 68}, + Name: "Grey69", + }, + { + Hex: "#afafd7", + Rgb: Rgb{175, 175, 215}, + Hsl: Hsl{240, 33, 76}, + Name: "LightSteelBlue3", + }, + { + Hex: "#afafff", + Rgb: Rgb{175, 175, 255}, + Hsl: Hsl{240, 100, 84}, + Name: "LightSteelBlue", + }, + { + Hex: "#afd700", + Rgb: Rgb{175, 215, 0}, + Hsl: Hsl{71.1627906976744, 100, 42}, + Name: "Yellow3", + }, + { + Hex: "#afd75f", + Rgb: Rgb{175, 215, 95}, + Hsl: Hsl{80, 60, 60}, + Name: "DarkOliveGreen3", + }, + { + Hex: "#afd787", + Rgb: Rgb{175, 215, 135}, + Hsl: Hsl{90, 50, 68}, + Name: "DarkSeaGreen3", + }, + { + Hex: "#afd7af", + Rgb: Rgb{175, 215, 175}, + Hsl: Hsl{120, 33, 76}, + Name: "DarkSeaGreen2", + }, + { + Hex: "#afd7d7", + Rgb: Rgb{175, 215, 215}, + Hsl: Hsl{180, 33, 76}, + Name: "LightCyan3", + }, + { + Hex: "#afd7ff", + Rgb: Rgb{175, 215, 255}, + Hsl: Hsl{210, 100, 84}, + Name: "LightSkyBlue1", + }, + { + Hex: "#afff00", + Rgb: Rgb{175, 255, 0}, + Hsl: Hsl{78.8235294117647, 100, 50}, + Name: "GreenYellow", + }, + { + Hex: "#afff5f", + Rgb: Rgb{175, 255, 95}, + Hsl: Hsl{90, 100, 68}, + Name: "DarkOliveGreen2", + }, + { + Hex: "#afff87", + Rgb: Rgb{175, 255, 135}, + Hsl: Hsl{100, 100, 76}, + Name: "PaleGreen1", + }, + { + Hex: "#afffaf", + Rgb: Rgb{175, 255, 175}, + Hsl: Hsl{120, 100, 84}, + Name: "DarkSeaGreen2", + }, + { + Hex: "#afffd7", + Rgb: Rgb{175, 255, 215}, + Hsl: Hsl{150, 100, 84}, + Name: "DarkSeaGreen1", + }, + { + Hex: "#afffff", + Rgb: Rgb{175, 255, 255}, + Hsl: Hsl{180, 100, 84}, + Name: "PaleTurquoise1", + }, + { + Hex: "#d70000", + Rgb: Rgb{215, 0, 0}, + Hsl: Hsl{0, 100, 42}, + Name: "Red3", + }, + { + Hex: "#d7005f", + Rgb: Rgb{215, 0, 95}, + Hsl: Hsl{333.488372093023, 100, 42}, + Name: "DeepPink3", + }, + { + Hex: "#d70087", + Rgb: Rgb{215, 0, 135}, + Hsl: Hsl{322.325581395349, 100, 42}, + Name: "DeepPink3", + }, + { + Hex: "#d700af", + Rgb: Rgb{215, 0, 175}, + Hsl: Hsl{311.162790697674, 100, 42}, + Name: "Magenta3", + }, + { + Hex: "#d700d7", + Rgb: Rgb{215, 0, 215}, + Hsl: Hsl{300, 100, 42}, + Name: "Magenta3", + }, + { + Hex: "#d700ff", + Rgb: Rgb{215, 0, 255}, + Hsl: Hsl{290.588235294118, 100, 50}, + Name: "Magenta2", + }, + { + Hex: "#d75f00", + Rgb: Rgb{215, 95, 0}, + Hsl: Hsl{26.5116279069767, 100, 42}, + Name: "DarkOrange3", + }, + { + Hex: "#d75f5f", + Rgb: Rgb{215, 95, 95}, + Hsl: Hsl{0, 60, 60}, + Name: "IndianRed", + }, + { + Hex: "#d75f87", + Rgb: Rgb{215, 95, 135}, + Hsl: Hsl{340, 60, 60}, + Name: "HotPink3", + }, + { + Hex: "#d75faf", + Rgb: Rgb{215, 95, 175}, + Hsl: Hsl{320, 60, 60}, + Name: "HotPink2", + }, + { + Hex: "#d75fd7", + Rgb: Rgb{215, 95, 215}, + Hsl: Hsl{300, 60, 60}, + Name: "Orchid", + }, + { + Hex: "#d75fff", + Rgb: Rgb{215, 95, 255}, + Hsl: Hsl{285, 100, 68}, + Name: "MediumOrchid1", + }, + { + Hex: "#d78700", + Rgb: Rgb{215, 135, 0}, + Hsl: Hsl{37.6744186046512, 100, 42}, + Name: "Orange3", + }, + { + Hex: "#d7875f", + Rgb: Rgb{215, 135, 95}, + Hsl: Hsl{20, 60, 60}, + Name: "LightSalmon3", + }, + { + Hex: "#d78787", + Rgb: Rgb{215, 135, 135}, + Hsl: Hsl{0, 50, 68}, + Name: "LightPink3", + }, + { + Hex: "#d787af", + Rgb: Rgb{215, 135, 175}, + Hsl: Hsl{330, 50, 68}, + Name: "Pink3", + }, + { + Hex: "#d787d7", + Rgb: Rgb{215, 135, 215}, + Hsl: Hsl{300, 50, 68}, + Name: "Plum3", + }, + { + Hex: "#d787ff", + Rgb: Rgb{215, 135, 255}, + Hsl: Hsl{280, 100, 76}, + Name: "Violet", + }, + { + Hex: "#d7af00", + Rgb: Rgb{215, 175, 0}, + Hsl: Hsl{48.8372093023256, 100, 42}, + Name: "Gold3", + }, + { + Hex: "#d7af5f", + Rgb: Rgb{215, 175, 95}, + Hsl: Hsl{40, 60, 60}, + Name: "LightGoldenrod3", + }, + { + Hex: "#d7af87", + Rgb: Rgb{215, 175, 135}, + Hsl: Hsl{30, 50, 68}, + Name: "Tan", + }, + { + Hex: "#d7afaf", + Rgb: Rgb{215, 175, 175}, + Hsl: Hsl{0, 33, 76}, + Name: "MistyRose3", + }, + { + Hex: "#d7afd7", + Rgb: Rgb{215, 175, 215}, + Hsl: Hsl{300, 33, 76}, + Name: "Thistle3", + }, + { + Hex: "#d7afff", + Rgb: Rgb{215, 175, 255}, + Hsl: Hsl{270, 100, 84}, + Name: "Plum2", + }, + { + Hex: "#d7d700", + Rgb: Rgb{215, 215, 0}, + Hsl: Hsl{60, 100, 42}, + Name: "Yellow3", + }, + { + Hex: "#d7d75f", + Rgb: Rgb{215, 215, 95}, + Hsl: Hsl{60, 60, 60}, + Name: "Khaki3", + }, + { + Hex: "#d7d787", + Rgb: Rgb{215, 215, 135}, + Hsl: Hsl{60, 50, 68}, + Name: "LightGoldenrod2", + }, + { + Hex: "#d7d7af", + Rgb: Rgb{215, 215, 175}, + Hsl: Hsl{60, 33, 76}, + Name: "LightYellow3", + }, + { + Hex: "#d7d7d7", + Rgb: Rgb{215, 215, 215}, + Hsl: Hsl{0, 0, 84}, + Name: "Grey84", + }, + { + Hex: "#d7d7ff", + Rgb: Rgb{215, 215, 255}, + Hsl: Hsl{240, 100, 92}, + Name: "LightSteelBlue1", + }, + { + Hex: "#d7ff00", + Rgb: Rgb{215, 255, 0}, + Hsl: Hsl{69.4117647058823, 100, 50}, + Name: "Yellow2", + }, + { + Hex: "#d7ff5f", + Rgb: Rgb{215, 255, 95}, + Hsl: Hsl{75, 100, 68}, + Name: "DarkOliveGreen1", + }, + { + Hex: "#d7ff87", + Rgb: Rgb{215, 255, 135}, + Hsl: Hsl{80, 100, 76}, + Name: "DarkOliveGreen1", + }, + { + Hex: "#d7ffaf", + Rgb: Rgb{215, 255, 175}, + Hsl: Hsl{90, 100, 84}, + Name: "DarkSeaGreen1", + }, + { + Hex: "#d7ffd7", + Rgb: Rgb{215, 255, 215}, + Hsl: Hsl{120, 100, 92}, + Name: "Honeydew2", + }, + { + Hex: "#d7ffff", + Rgb: Rgb{215, 255, 255}, + Hsl: Hsl{180, 100, 92}, + Name: "LightCyan1", + }, + { + Hex: "#ff0000", + Rgb: Rgb{255, 0, 0}, + Hsl: Hsl{0, 100, 50}, + Name: "Red1", + }, + { + Hex: "#ff005f", + Rgb: Rgb{255, 0, 95}, + Hsl: Hsl{337.647058823529, 100, 50}, + Name: "DeepPink2", + }, + { + Hex: "#ff0087", + Rgb: Rgb{255, 0, 135}, + Hsl: Hsl{328.235294117647, 100, 50}, + Name: "DeepPink1", + }, + { + Hex: "#ff00af", + Rgb: Rgb{255, 0, 175}, + Hsl: Hsl{318.823529411765, 100, 50}, + Name: "DeepPink1", + }, + { + Hex: "#ff00d7", + Rgb: Rgb{255, 0, 215}, + Hsl: Hsl{309.411764705882, 100, 50}, + Name: "Magenta2", + }, + { + Hex: "#ff00ff", + Rgb: Rgb{255, 0, 255}, + Hsl: Hsl{300, 100, 50}, + Name: "Magenta1", + }, + { + Hex: "#ff5f00", + Rgb: Rgb{255, 95, 0}, + Hsl: Hsl{22.3529411764706, 100, 50}, + Name: "OrangeRed1", + }, + { + Hex: "#ff5f5f", + Rgb: Rgb{255, 95, 95}, + Hsl: Hsl{0, 100, 68}, + Name: "IndianRed1", + }, + { + Hex: "#ff5f87", + Rgb: Rgb{255, 95, 135}, + Hsl: Hsl{345, 100, 68}, + Name: "IndianRed1", + }, + { + Hex: "#ff5faf", + Rgb: Rgb{255, 95, 175}, + Hsl: Hsl{330, 100, 68}, + Name: "HotPink", + }, + { + Hex: "#ff5fd7", + Rgb: Rgb{255, 95, 215}, + Hsl: Hsl{315, 100, 68}, + Name: "HotPink", + }, + { + Hex: "#ff5fff", + Rgb: Rgb{255, 95, 255}, + Hsl: Hsl{300, 100, 68}, + Name: "MediumOrchid1", + }, + { + Hex: "#ff8700", + Rgb: Rgb{255, 135, 0}, + Hsl: Hsl{31.7647058823529, 100, 50}, + Name: "DarkOrange", + }, + { + Hex: "#ff875f", + Rgb: Rgb{255, 135, 95}, + Hsl: Hsl{15, 100, 68}, + Name: "Salmon1", + }, + { + Hex: "#ff8787", + Rgb: Rgb{255, 135, 135}, + Hsl: Hsl{0, 100, 76}, + Name: "LightCoral", + }, + { + Hex: "#ff87af", + Rgb: Rgb{255, 135, 175}, + Hsl: Hsl{340, 100, 76}, + Name: "PaleVioletRed1", + }, + { + Hex: "#ff87d7", + Rgb: Rgb{255, 135, 215}, + Hsl: Hsl{320, 100, 76}, + Name: "Orchid2", + }, + { + Hex: "#ff87ff", + Rgb: Rgb{255, 135, 255}, + Hsl: Hsl{300, 100, 76}, + Name: "Orchid1", + }, + { + Hex: "#ffaf00", + Rgb: Rgb{255, 175, 0}, + Hsl: Hsl{41.1764705882353, 100, 50}, + Name: "Orange1", + }, + { + Hex: "#ffaf5f", + Rgb: Rgb{255, 175, 95}, + Hsl: Hsl{30, 100, 68}, + Name: "SandyBrown", + }, + { + Hex: "#ffaf87", + Rgb: Rgb{255, 175, 135}, + Hsl: Hsl{20, 100, 76}, + Name: "LightSalmon1", + }, + { + Hex: "#ffafaf", + Rgb: Rgb{255, 175, 175}, + Hsl: Hsl{0, 100, 84}, + Name: "LightPink1", + }, + { + Hex: "#ffafd7", + Rgb: Rgb{255, 175, 215}, + Hsl: Hsl{330, 100, 84}, + Name: "Pink1", + }, + { + Hex: "#ffafff", + Rgb: Rgb{255, 175, 255}, + Hsl: Hsl{300, 100, 84}, + Name: "Plum1", + }, + { + Hex: "#ffd700", + Rgb: Rgb{255, 215, 0}, + Hsl: Hsl{50.5882352941176, 100, 50}, + Name: "Gold1", + }, + { + Hex: "#ffd75f", + Rgb: Rgb{255, 215, 95}, + Hsl: Hsl{45, 100, 68}, + Name: "LightGoldenrod2", + }, + { + Hex: "#ffd787", + Rgb: Rgb{255, 215, 135}, + Hsl: Hsl{40, 100, 76}, + Name: "LightGoldenrod2", + }, + { + Hex: "#ffd7af", + Rgb: Rgb{255, 215, 175}, + Hsl: Hsl{30, 100, 84}, + Name: "NavajoWhite1", + }, + { + Hex: "#ffd7d7", + Rgb: Rgb{255, 215, 215}, + Hsl: Hsl{0, 100, 92}, + Name: "MistyRose1", + }, + { + Hex: "#ffd7ff", + Rgb: Rgb{255, 215, 255}, + Hsl: Hsl{300, 100, 92}, + Name: "Thistle1", + }, + { + Hex: "#ffff00", + Rgb: Rgb{255, 255, 0}, + Hsl: Hsl{60, 100, 50}, + Name: "Yellow1", + }, + { + Hex: "#ffff5f", + Rgb: Rgb{255, 255, 95}, + Hsl: Hsl{60, 100, 68}, + Name: "LightGoldenrod1", + }, + { + Hex: "#ffff87", + Rgb: Rgb{255, 255, 135}, + Hsl: Hsl{60, 100, 76}, + Name: "Khaki1", + }, + { + Hex: "#ffffaf", + Rgb: Rgb{255, 255, 175}, + Hsl: Hsl{60, 100, 84}, + Name: "Wheat1", + }, + { + Hex: "#ffffd7", + Rgb: Rgb{255, 255, 215}, + Hsl: Hsl{60, 100, 92}, + Name: "Cornsilk1", + }, + { + Hex: "#ffffff", + Rgb: Rgb{255, 255, 255}, + Hsl: Hsl{0, 0, 100}, + Name: "Grey100", + }, + { + Hex: "#080808", + Rgb: Rgb{8, 8, 8}, + Hsl: Hsl{0, 0, 3}, + Name: "Grey3", + }, + { + Hex: "#121212", + Rgb: Rgb{18, 18, 18}, + Hsl: Hsl{0, 0, 7}, + Name: "Grey7", + }, + { + Hex: "#1c1c1c", + Rgb: Rgb{28, 28, 28}, + Hsl: Hsl{0, 0, 10}, + Name: "Grey11", + }, + { + Hex: "#262626", + Rgb: Rgb{38, 38, 38}, + Hsl: Hsl{0, 0, 14}, + Name: "Grey15", + }, + { + Hex: "#303030", + Rgb: Rgb{48, 48, 48}, + Hsl: Hsl{0, 0, 18}, + Name: "Grey19", + }, + { + Hex: "#3a3a3a", + Rgb: Rgb{58, 58, 58}, + Hsl: Hsl{0, 0, 22}, + Name: "Grey23", + }, + { + Hex: "#444444", + Rgb: Rgb{68, 68, 68}, + Hsl: Hsl{0, 0, 26}, + Name: "Grey27", + }, + { + Hex: "#4e4e4e", + Rgb: Rgb{78, 78, 78}, + Hsl: Hsl{0, 0, 30}, + Name: "Grey30", + }, + { + Hex: "#585858", + Rgb: Rgb{88, 88, 88}, + Hsl: Hsl{0, 0, 34}, + Name: "Grey35", + }, + { + Hex: "#626262", + Rgb: Rgb{98, 98, 98}, + Hsl: Hsl{0, 0, 37}, + Name: "Grey39", + }, + { + Hex: "#6c6c6c", + Rgb: Rgb{108, 108, 108}, + Hsl: Hsl{0, 0, 40}, + Name: "Grey42", + }, + { + Hex: "#767676", + Rgb: Rgb{118, 118, 118}, + Hsl: Hsl{0, 0, 46}, + Name: "Grey46", + }, + { + Hex: "#808080", + Rgb: Rgb{128, 128, 128}, + Hsl: Hsl{0, 0, 50}, + Name: "Grey50", + }, + { + Hex: "#8a8a8a", + Rgb: Rgb{138, 138, 138}, + Hsl: Hsl{0, 0, 54}, + Name: "Grey54", + }, + { + Hex: "#949494", + Rgb: Rgb{148, 148, 148}, + Hsl: Hsl{0, 0, 58}, + Name: "Grey58", + }, + { + Hex: "#9e9e9e", + Rgb: Rgb{158, 158, 158}, + Hsl: Hsl{0, 0, 61}, + Name: "Grey62", + }, + { + Hex: "#a8a8a8", + Rgb: Rgb{168, 168, 168}, + Hsl: Hsl{0, 0, 65}, + Name: "Grey66", + }, + { + Hex: "#b2b2b2", + Rgb: Rgb{178, 178, 178}, + Hsl: Hsl{0, 0, 69}, + Name: "Grey70", + }, + { + Hex: "#bcbcbc", + Rgb: Rgb{188, 188, 188}, + Hsl: Hsl{0, 0, 73}, + Name: "Grey74", + }, + { + Hex: "#c6c6c6", + Rgb: Rgb{198, 198, 198}, + Hsl: Hsl{0, 0, 77}, + Name: "Grey78", + }, + { + Hex: "#d0d0d0", + Rgb: Rgb{208, 208, 208}, + Hsl: Hsl{0, 0, 81}, + Name: "Grey82", + }, + { + Hex: "#dadada", + Rgb: Rgb{218, 218, 218}, + Hsl: Hsl{0, 0, 85}, + Name: "Grey85", + }, + { + Hex: "#e4e4e4", + Rgb: Rgb{228, 228, 228}, + Hsl: Hsl{0, 0, 89}, + Name: "Grey89", + }, + { + Hex: "#eeeeee", + Rgb: Rgb{238, 238, 238}, + Hsl: Hsl{0, 0, 93}, + Name: "Grey93", + }, +} diff --git a/v2/pkg/menu/contextmenu.go b/v2/pkg/menu/contextmenu.go new file mode 100644 index 000000000..e24b04067 --- /dev/null +++ b/v2/pkg/menu/contextmenu.go @@ -0,0 +1,13 @@ +package menu + +type ContextMenu struct { + ID string + Menu *Menu +} + +func NewContextMenu(ID string, menu *Menu) *ContextMenu { + return &ContextMenu{ + ID: ID, + Menu: menu, + } +} diff --git a/v2/pkg/menu/keys/keys.go b/v2/pkg/menu/keys/keys.go new file mode 100644 index 000000000..73bc9414f --- /dev/null +++ b/v2/pkg/menu/keys/keys.go @@ -0,0 +1,106 @@ +package keys + +import ( + "fmt" + "strings" +) + +// Modifier is actually a string +type Modifier string + +const ( + // CmdOrCtrlKey represents Command on Mac and Control on other platforms + CmdOrCtrlKey Modifier = "cmdorctrl" + // OptionOrAltKey represents Option on Mac and Alt on other platforms + OptionOrAltKey Modifier = "optionoralt" + // ShiftKey represents the shift key on all systems + ShiftKey Modifier = "shift" + // SuperKey represents Command on Mac and the Windows key on the other platforms + //SuperKey Modifier = "super" + // ControlKey represents the control key on all systems + ControlKey Modifier = "ctrl" +) + +var modifierMap = map[string]Modifier{ + "cmdorctrl": CmdOrCtrlKey, + "optionoralt": OptionOrAltKey, + "shift": ShiftKey, + //"super": SuperKey, + "ctrl": ControlKey, +} + +func parseModifier(text string) (*Modifier, error) { + lowertext := strings.ToLower(text) + result, valid := modifierMap[lowertext] + if !valid { + return nil, fmt.Errorf("'%s' is not a valid modifier", text) + } + + return &result, nil +} + +// Accelerator holds the keyboard shortcut for a menu item +type Accelerator struct { + Key string + Modifiers []Modifier +} + +// Key creates a standard key Accelerator +func Key(key string) *Accelerator { + return &Accelerator{ + Key: strings.ToLower(key), + } +} + +// CmdOrCtrl creates a 'CmdOrCtrl' Accelerator +func CmdOrCtrl(key string) *Accelerator { + return &Accelerator{ + Key: strings.ToLower(key), + Modifiers: []Modifier{CmdOrCtrlKey}, + } +} + +// OptionOrAlt creates a 'OptionOrAlt' Accelerator +func OptionOrAlt(key string) *Accelerator { + return &Accelerator{ + Key: strings.ToLower(key), + Modifiers: []Modifier{OptionOrAltKey}, + } +} + +// Shift creates a 'Shift' Accelerator +func Shift(key string) *Accelerator { + return &Accelerator{ + Key: strings.ToLower(key), + Modifiers: []Modifier{ShiftKey}, + } +} + +// Control creates a 'Control' Accelerator +func Control(key string) *Accelerator { + return &Accelerator{ + Key: strings.ToLower(key), + Modifiers: []Modifier{ControlKey}, + } +} + +// +//// Super creates a 'Super' Accelerator +//func Super(key string) *Accelerator { +// return &Accelerator{ +// Key: strings.ToLower(key), +// Modifiers: []Modifier{SuperKey}, +// } +//} + +// Combo creates an Accelerator with multiple Modifiers +func Combo(key string, modifier1 Modifier, modifier2 Modifier, rest ...Modifier) *Accelerator { + result := &Accelerator{ + Key: key, + Modifiers: []Modifier{modifier1, modifier2}, + } + for _, extra := range rest { + result.Modifiers = append(result.Modifiers, extra) + } + return result +} diff --git a/v2/pkg/menu/keys/parser.go b/v2/pkg/menu/keys/parser.go new file mode 100644 index 000000000..91a05783d --- /dev/null +++ b/v2/pkg/menu/keys/parser.go @@ -0,0 +1,90 @@ +package keys + +import ( + "fmt" + "strconv" + "strings" + + "github.com/leaanthony/slicer" +) + +var namedKeys = slicer.String([]string{"backspace", "tab", "return", "enter", "escape", "left", "right", "up", "down", "space", "delete", "home", "end", "page up", "page down", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", "f32", "f33", "f34", "f35", "numlock"}) + +func parseKey(key string) (string, bool) { + + // Lowercase! + key = strings.ToLower(key) + + // Check special case + if key == "plus" { + return "+", true + } + + // Handle named keys + if namedKeys.Contains(key) { + return key, true + } + + // Check we only have a single character + if len(key) != 1 { + return "", false + } + + runeKey := rune(key[0]) + + // This may be too inclusive + if strconv.IsPrint(runeKey) { + return key, true + } + + return "", false + +} + +func Parse(shortcut string) (*Accelerator, error) { + + var result Accelerator + + // Split the shortcut by + + components := strings.Split(shortcut, "+") + + // If we only have one it should be a key + // We require components + if len(components) == 0 { + return nil, fmt.Errorf("no components given to validateComponents") + } + + // Keep track of modifiers we have processed + var modifiersProcessed slicer.StringSlicer + + // Check components + for index, component := range components { + + // If last component + if index == len(components)-1 { + processedkey, validKey := parseKey(component) + if !validKey { + return nil, fmt.Errorf("'%s' is not a valid key", component) + } + result.Key = processedkey + continue + } + + // Not last component - needs to be modifier + lowercaseComponent := strings.ToLower(component) + thisModifier, valid := modifierMap[lowercaseComponent] + if !valid { + return nil, fmt.Errorf("'%s' is not a valid modifier", component) + } + // Needs to be unique + if modifiersProcessed.Contains(lowercaseComponent) { + return nil, fmt.Errorf("Modifier '%s' is defined twice for shortcut: %s", component, shortcut) + } + + // Save this data + result.Modifiers = append(result.Modifiers, thisModifier) + modifiersProcessed.Add(lowercaseComponent) + } + + return &result, nil +} diff --git a/v2/pkg/menu/keys/parser_test.go b/v2/pkg/menu/keys/parser_test.go new file mode 100644 index 000000000..703861da1 --- /dev/null +++ b/v2/pkg/menu/keys/parser_test.go @@ -0,0 +1,37 @@ +package keys + +import ( + "testing" + + "github.com/matryer/is" +) + +func TestParse(t *testing.T) { + + i := is.New(t) + + type args struct { + Input string + Expected *Accelerator + } + + gooddata := []args{ + {"CmdOrCtrl+A", CmdOrCtrl("A")}, + {"SHIFT+.", Shift(".")}, + {"CTRL+plus", Control("+")}, + {"CTRL+SHIFT+escApe", Combo("escape", ControlKey, ShiftKey)}, + {";", Key(";")}, + {"OptionOrAlt+Page Down", OptionOrAlt("Page Down")}, + } + for _, tt := range gooddata { + result, err := Parse(tt.Input) + i.NoErr(err) + i.Equal(result, tt.Expected) + } + baddata := []string{"CmdOrCrl+A", "SHIT+.", "CTL+plus", "CTRL+SHIF+esApe", "escap", "Sper+Tab", "OptionOrAlt"} + for _, d := range baddata { + result, err := Parse(d) + i.True(err != nil) + i.Equal(result, "") + } +} diff --git a/v2/pkg/menu/keys/stringify.go b/v2/pkg/menu/keys/stringify.go new file mode 100644 index 000000000..ccc8c5e9e --- /dev/null +++ b/v2/pkg/menu/keys/stringify.go @@ -0,0 +1,40 @@ +package keys + +import ( + "github.com/leaanthony/slicer" + "strings" +) + +var modifierStringMap = map[string]map[Modifier]string{ + "windows": { + CmdOrCtrlKey: "Ctrl", + ControlKey: "Ctrl", + OptionOrAltKey: "Alt", + ShiftKey: "Shift", + //SuperKey: "Win", + }, + "darwin": { + CmdOrCtrlKey: "Cmd", + ControlKey: "Ctrl", + OptionOrAltKey: "Option", + ShiftKey: "Shift", + //SuperKey: "Cmd", + }, + "linux": { + CmdOrCtrlKey: "Ctrl", + ControlKey: "Ctrl", + OptionOrAltKey: "Alt", + ShiftKey: "Shift", + //SuperKey: "Super", + }, +} + +func Stringify(accelerator *Accelerator, platform string) string { + result := slicer.String() + for _, modifier := range accelerator.Modifiers { + result.Add(modifierStringMap[platform][modifier]) + } + result.Deduplicate() + result.Add(strings.ToUpper(accelerator.Key)) + return result.Join("+") +} diff --git a/v2/pkg/menu/keys/stringify_test.go b/v2/pkg/menu/keys/stringify_test.go new file mode 100644 index 000000000..e6ba26221 --- /dev/null +++ b/v2/pkg/menu/keys/stringify_test.go @@ -0,0 +1,75 @@ +package keys + +import ( + "strconv" + "testing" +) + +func TestStringify(t *testing.T) { + + const Windows = "windows" + const Mac = "darwin" + const Linux = "linux" + tests := []struct { + arg *Accelerator + want string + platform string + }{ + // Single Keys + {Key("a"), "A", Windows}, + {Key(""), "", Windows}, + {Key("?"), "?", Windows}, + {Key("a"), "A", Mac}, + {Key(""), "", Mac}, + {Key("?"), "?", Mac}, + {Key("a"), "A", Linux}, + {Key(""), "", Linux}, + {Key("?"), "?", Linux}, + + // Single modifier + {Control("a"), "Ctrl+A", Windows}, + {Control("a"), "Ctrl+A", Mac}, + {Control("a"), "Ctrl+A", Linux}, + {CmdOrCtrl("a"), "Ctrl+A", Windows}, + {CmdOrCtrl("a"), "Cmd+A", Mac}, + {CmdOrCtrl("a"), "Ctrl+A", Linux}, + {Shift("a"), "Shift+A", Windows}, + {Shift("a"), "Shift+A", Mac}, + {Shift("a"), "Shift+A", Linux}, + {OptionOrAlt("a"), "Alt+A", Windows}, + {OptionOrAlt("a"), "Option+A", Mac}, + {OptionOrAlt("a"), "Alt+A", Linux}, + //{Super("a"), "Win+A", Windows}, + //{Super("a"), "Cmd+A", Mac}, + //{Super("a"), "Super+A", Linux}, + + // Dual Combo non duplicate + {Combo("a", ControlKey, OptionOrAltKey), "Ctrl+Alt+A", Windows}, + {Combo("a", ControlKey, OptionOrAltKey), "Ctrl+Option+A", Mac}, + {Combo("a", ControlKey, OptionOrAltKey), "Ctrl+Alt+A", Linux}, + {Combo("a", CmdOrCtrlKey, OptionOrAltKey), "Ctrl+Alt+A", Windows}, + {Combo("a", CmdOrCtrlKey, OptionOrAltKey), "Cmd+Option+A", Mac}, + {Combo("a", CmdOrCtrlKey, OptionOrAltKey), "Ctrl+Alt+A", Linux}, + {Combo("a", ShiftKey, OptionOrAltKey), "Shift+Alt+A", Windows}, + {Combo("a", ShiftKey, OptionOrAltKey), "Shift+Option+A", Mac}, + {Combo("a", ShiftKey, OptionOrAltKey), "Shift+Alt+A", Linux}, + //{Combo("a", SuperKey, OptionOrAltKey), "Win+Alt+A", Windows}, + //{Combo("a", SuperKey, OptionOrAltKey), "Cmd+Option+A", Mac}, + //{Combo("a", SuperKey, OptionOrAltKey), "Super+Alt+A", Linux}, + + // Combo duplicate + {Combo("a", OptionOrAltKey, OptionOrAltKey), "Alt+A", Windows}, + {Combo("a", OptionOrAltKey, OptionOrAltKey), "Option+A", Mac}, + {Combo("a", OptionOrAltKey, OptionOrAltKey), "Alt+A", Linux}, + //{Combo("a", OptionOrAltKey, SuperKey, OptionOrAltKey), "Alt+Win+A", Windows}, + //{Combo("a", OptionOrAltKey, SuperKey, OptionOrAltKey), "Option+Cmd+A", Mac}, + //{Combo("a", OptionOrAltKey, SuperKey, OptionOrAltKey), "Alt+Super+A", Linux}, + } + for index, tt := range tests { + t.Run(strconv.Itoa(index), func(t *testing.T) { + if got := Stringify(tt.arg, tt.platform); got != tt.want { + t.Errorf("Stringify() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/v2/pkg/menu/mac.go b/v2/pkg/menu/mac.go new file mode 100644 index 000000000..0889e5c42 --- /dev/null +++ b/v2/pkg/menu/mac.go @@ -0,0 +1,12 @@ +package menu + +/* +// DefaultMacMenu returns a default menu including the default +// Application and Edit menus. Use `.Append()` to add to it. +func DefaultMacMenu() *Menu { + return NewMenuFromItems( + AppMenu(), + EditMenu(), + ) +} +*/ diff --git a/v2/pkg/menu/menu.go b/v2/pkg/menu/menu.go new file mode 100644 index 000000000..be04820d3 --- /dev/null +++ b/v2/pkg/menu/menu.go @@ -0,0 +1,42 @@ +package menu + +type Menu struct { + Items []*MenuItem +} + +func NewMenu() *Menu { + return &Menu{} +} + +func (m *Menu) Append(item *MenuItem) { + m.Items = append(m.Items, item) +} + +// Merge will append the items in the given menu +// into this menu +func (m *Menu) Merge(menu *Menu) { + for _, item := range menu.Items { + m.Items = append(m.Items, item) + } +} + +func (m *Menu) Prepend(item *MenuItem) { + m.Items = append([]*MenuItem{item}, m.Items...) +} + +func NewMenuFromItems(first *MenuItem, rest ...*MenuItem) *Menu { + + var result = NewMenu() + result.Append(first) + for _, item := range rest { + result.Append(item) + } + + return result +} + +func (m *Menu) setParent(menuItem *MenuItem) { + for _, item := range m.Items { + item.parent = menuItem + } +} diff --git a/v2/pkg/menu/menuitem.go b/v2/pkg/menu/menuitem.go new file mode 100644 index 000000000..2a55b5fbc --- /dev/null +++ b/v2/pkg/menu/menuitem.go @@ -0,0 +1,282 @@ +package menu + +import ( + "sync" + + "github.com/wailsapp/wails/v2/pkg/menu/keys" +) + +// MenuItem represents a menuitem contained in a menu +type MenuItem struct { + // Label is what appears as the menu text + Label string + // Role is a predefined menu type + //Role Role `json:"Role,omitempty"` + // Accelerator holds a representation of a key binding + Accelerator *keys.Accelerator `json:"Accelerator,omitempty"` + // Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu + Type Type + // Disabled makes the item unselectable + Disabled bool + // Hidden ensures that the item is not shown in the menu + Hidden bool + // Checked indicates if the item is selected (used by Checkbox and Radio types only) + Checked bool + // Submenu contains a list of menu items that will be shown as a submenu + //SubMenu []*MenuItem `json:"SubMenu,omitempty"` + SubMenu *Menu `json:"SubMenu,omitempty"` + + // Callback function when menu clicked + Click Callback `json:"-"` + /* + // Text Colour + RGBA string + + // Font + FontSize int + FontName string + + // Image - base64 image data + Image string + + // MacTemplateImage indicates that on a Mac, this image is a template image + MacTemplateImage bool + + // MacAlternate indicates that this item is an alternative to the previous menu item + MacAlternate bool + + // Tooltip + Tooltip string + */ + // This holds the menu item's parent. + parent *MenuItem + + // Used for locking when removing elements + removeLock sync.Mutex +} + +// Parent returns the parent of the menu item. +// If it is a top level menu then it returns nil. +func (m *MenuItem) Parent() *MenuItem { + return m.parent +} + +// Append will attempt to append the given menu item to +// this item's submenu items. If this menu item is not a +// submenu, then this method will not add the item and +// simply return false. +func (m *MenuItem) Append(item *MenuItem) bool { + if !m.isSubMenu() { + return false + } + item.parent = m + m.SubMenu.Append(item) + return true +} + +// Prepend will attempt to prepend the given menu item to +// this item's submenu items. If this menu item is not a +// submenu, then this method will not add the item and +// simply return false. +func (m *MenuItem) Prepend(item *MenuItem) bool { + if !m.isSubMenu() { + return false + } + item.parent = m + m.SubMenu.Prepend(item) + return true +} + +func (m *MenuItem) Remove() { + // Iterate my parent's children + m.Parent().removeChild(m) +} + +func (m *MenuItem) removeChild(item *MenuItem) { + m.removeLock.Lock() + for index, child := range m.SubMenu.Items { + if item == child { + m.SubMenu.Items = append(m.SubMenu.Items[:index], m.SubMenu.Items[index+1:]...) + } + } + m.removeLock.Unlock() +} + +// InsertAfter attempts to add the given item after this item in the parent +// menu. If there is no parent menu (we are a top level menu) then false is +// returned +func (m *MenuItem) InsertAfter(item *MenuItem) bool { + + // We need to find my parent + if m.parent == nil { + return false + } + + // Get my parent to insert the item + return m.parent.insertNewItemAfterGivenItem(m, item) +} + +// InsertBefore attempts to add the given item before this item in the parent +// menu. If there is no parent menu (we are a top level menu) then false is +// returned +func (m *MenuItem) InsertBefore(item *MenuItem) bool { + + // We need to find my parent + if m.parent == nil { + return false + } + + // Get my parent to insert the item + return m.parent.insertNewItemBeforeGivenItem(m, item) +} + +// insertNewItemAfterGivenItem will insert the given item after the given target +// in this item's submenu. If we are not a submenu, +// then something bad has happened :/ +func (m *MenuItem) insertNewItemAfterGivenItem(target *MenuItem, + newItem *MenuItem) bool { + + if !m.isSubMenu() { + return false + } + + // Find the index of the target + targetIndex := m.getItemIndex(target) + if targetIndex == -1 { + return false + } + + // Insert element into slice + return m.insertItemAtIndex(targetIndex+1, newItem) +} + +// insertNewItemBeforeGivenItem will insert the given item before the given +// target in this item's submenu. If we are not a submenu, then something bad +// has happened :/ +func (m *MenuItem) insertNewItemBeforeGivenItem(target *MenuItem, + newItem *MenuItem) bool { + + if !m.isSubMenu() { + return false + } + + // Find the index of the target + targetIndex := m.getItemIndex(target) + if targetIndex == -1 { + return false + } + + // Insert element into slice + return m.insertItemAtIndex(targetIndex, newItem) +} + +func (m *MenuItem) isSubMenu() bool { + return m.Type == SubmenuType +} + +// getItemIndex returns the index of the given target relative to this menu +func (m *MenuItem) getItemIndex(target *MenuItem) int { + + // This should only be called on submenus + if !m.isSubMenu() { + return -1 + } + + // hunt down that bad boy + for index, item := range m.SubMenu.Items { + if item == target { + return index + } + } + + return -1 +} + +// insertItemAtIndex attempts to insert the given item into the submenu at +// the given index +// Credit: https://stackoverflow.com/a/61822301 +func (m *MenuItem) insertItemAtIndex(index int, target *MenuItem) bool { + + // If index is OOB, return false + if index > len(m.SubMenu.Items) { + return false + } + + // Save parent reference + target.parent = m + + // If index is last item, then just regular append + if index == len(m.SubMenu.Items) { + m.SubMenu.Items = append(m.SubMenu.Items, target) + return true + } + + m.SubMenu.Items = append(m.SubMenu.Items[:index+1], m.SubMenu.Items[index:]...) + m.SubMenu.Items[index] = target + return true +} + +// Text is a helper to create basic Text menu items +func Text(label string, accelerator *keys.Accelerator, click Callback) *MenuItem { + return &MenuItem{ + Label: label, + Type: TextType, + Accelerator: accelerator, + Click: click, + } +} + +// Separator provides a menu separator +func Separator() *MenuItem { + return &MenuItem{ + Type: SeparatorType, + } +} + +// Radio is a helper to create basic Radio menu items with an accelerator +func Radio(label string, selected bool, accelerator *keys.Accelerator, click Callback) *MenuItem { + return &MenuItem{ + Label: label, + Type: RadioType, + Checked: selected, + Accelerator: accelerator, + Click: click, + } +} + +// Checkbox is a helper to create basic Checkbox menu items +func Checkbox(label string, checked bool, accelerator *keys.Accelerator, click Callback) *MenuItem { + return &MenuItem{ + Label: label, + Type: CheckboxType, + Checked: checked, + Accelerator: accelerator, + Click: click, + } +} + +// SubMenu is a helper to create Submenus +func SubMenu(label string, menu *Menu) *MenuItem { + result := &MenuItem{ + Label: label, + SubMenu: menu, + Type: SubmenuType, + } + + menu.setParent(result) + + return result +} + +// SubMenuWithID is a helper to create Submenus with an ID +func SubMenuWithID(label string, menu *Menu) *MenuItem { + result := &MenuItem{ + Label: label, + SubMenu: menu, + Type: SubmenuType, + } + + menu.setParent(result) + + return result +} diff --git a/v2/pkg/menu/menuroles.go b/v2/pkg/menu/menuroles.go new file mode 100644 index 000000000..55f666a75 --- /dev/null +++ b/v2/pkg/menu/menuroles.go @@ -0,0 +1,206 @@ +// Package menu provides all the functions and structs related to menus in a Wails application. +// Heavily inspired by Electron (c) 2013-2020 Github Inc. +// Electron License: https://github.com/electron/electron/blob/master/LICENSE +package menu + +/* +type Role string + +const ( + AboutRole Role = "about" + UndoRole Role = "undo" + RedoRole Role = "redo" + CutRole Role = "cut" + CopyRole Role = "copy" + PasteRole Role = "paste" + PasteAndMatchStyleRole Role = "pasteAndMatchStyle" + SelectAllRole Role = "selectAll" + DeleteRole Role = "delete" + MinimizeRole Role = "minimize" + QuitRole Role = "quit" + TogglefullscreenRole Role = "togglefullscreen" + FileMenuRole Role = "fileMenu" + EditMenuRole Role = "editMenu" + ViewMenuRole Role = "viewMenu" + WindowMenuRole Role = "windowMenu" + AppMenuRole Role = "appMenu" + HideRole Role = "hide" + HideOthersRole Role = "hideOthers" + UnhideRole Role = "unhide" + FrontRole Role = "front" + ZoomRole Role = "zoom" + WindowSubMenuRole Role = "windowSubMenu" + HelpSubMenuRole Role = "helpSubMenu" + SeparatorItemRole Role = "separatorItem" +) + +// About provides a MenuItem with the About role +func About() *MenuItem { + return &MenuItem{ + Role: AboutRole, + } +} + +// Undo provides a MenuItem with the Undo role +func Undo() *MenuItem { + return &MenuItem{ + Role: UndoRole, + } +} + +// Redo provides a MenuItem with the Redo role +func Redo() *MenuItem { + return &MenuItem{ + Role: RedoRole, + } +} + +// Cut provides a MenuItem with the Cut role +func Cut() *MenuItem { + return &MenuItem{ + Role: CutRole, + } +} + +// Copy provides a MenuItem with the Copy role +func Copy() *MenuItem { + return &MenuItem{ + Role: CopyRole, + } +} + +// Paste provides a MenuItem with the Paste role +func Paste() *MenuItem { + return &MenuItem{ + Role: PasteRole, + } +} + +// PasteAndMatchStyle provides a MenuItem with the PasteAndMatchStyle role +func PasteAndMatchStyle() *MenuItem { + return &MenuItem{ + Role: PasteAndMatchStyleRole, + } +} + +// SelectAll provides a MenuItem with the SelectAll role +func SelectAll() *MenuItem { + return &MenuItem{ + Role: SelectAllRole, + } +} + +// Delete provides a MenuItem with the Delete role +func Delete() *MenuItem { + return &MenuItem{ + Role: DeleteRole, + } +} + +// Minimize provides a MenuItem with the Minimize role +func Minimize() *MenuItem { + return &MenuItem{ + Role: MinimizeRole, + } +} + +// Quit provides a MenuItem with the Quit role +func Quit() *MenuItem { + return &MenuItem{ + Role: QuitRole, + } +} + +// Togglefullscreen provides a MenuItem with the Togglefullscreen role +func Togglefullscreen() *MenuItem { + return &MenuItem{ + Role: TogglefullscreenRole, + } +} + +// FileMenu provides a MenuItem with the whole default "File" menu (Close / Quit) +func FileMenu() *MenuItem { + return &MenuItem{ + Role: FileMenuRole, + } +} + +// EditMenu provides a MenuItem with the whole default "Edit" menu (Undo, Copy, etc.). +func EditMenu() *MenuItem { + return &MenuItem{ + Role: EditMenuRole, + } +} + +// ViewMenu provides a MenuItem with the whole default "View" menu (Reload, Toggle Developer Tools, etc.) +func ViewMenu() *MenuItem { + return &MenuItem{ + Role: ViewMenuRole, + } +} + +// WindowMenu provides a MenuItem with the whole default "Window" menu (Minimize, Zoom, etc.). +func WindowMenu() *MenuItem { + return &MenuItem{ + Role: WindowMenuRole, + } +} + +// These roles are Mac only + +// AppMenu provides a MenuItem with the whole default "App" menu (About, Services, etc.) +func AppMenu() *MenuItem { + return &MenuItem{ + Role: AppMenuRole, + } +} + +// Hide provides a MenuItem that maps to the hide action. +func Hide() *MenuItem { + return &MenuItem{ + Role: HideRole, + } +} + +// HideOthers provides a MenuItem that maps to the hideOtherApplications action. +func HideOthers() *MenuItem { + return &MenuItem{ + Role: HideOthersRole, + } +} + +// Unhide provides a MenuItem that maps to the unhideAllApplications action. +func Unhide() *MenuItem { + return &MenuItem{ + Role: UnhideRole, + } +} + +// Front provides a MenuItem that maps to the arrangeInFront action. +func Front() *MenuItem { + return &MenuItem{ + Role: FrontRole, + } +} + +// Zoom provides a MenuItem that maps to the performZoom action. +func Zoom() *MenuItem { + return &MenuItem{ + Role: ZoomRole, + } +} + +// WindowSubMenu provides a MenuItem with the "Window" submenu. +func WindowSubMenu() *MenuItem { + return &MenuItem{ + Role: WindowSubMenuRole, + } +} + +// HelpSubMenu provides a MenuItem with the "Help" submenu. +func HelpSubMenu() *MenuItem { + return &MenuItem{ + Role: HelpSubMenuRole, + } +} +*/ diff --git a/v2/pkg/menu/styledlabel.go b/v2/pkg/menu/styledlabel.go new file mode 100644 index 000000000..9179cd2dc --- /dev/null +++ b/v2/pkg/menu/styledlabel.go @@ -0,0 +1,256 @@ +package menu + +import ( + "fmt" + "strconv" + "strings" +) + +type TextStyle int + +const ( + Bold TextStyle = 1 << 0 + Faint TextStyle = 1 << 1 + Italic TextStyle = 1 << 2 + Blinking TextStyle = 1 << 3 + Inversed TextStyle = 1 << 4 + Invisible TextStyle = 1 << 5 + Underlined TextStyle = 1 << 6 + Strikethrough TextStyle = 1 << 7 +) + +type StyledText struct { + Label string + FgCol *Col + BgCol *Col + Style TextStyle +} + +func (s *StyledText) Bold() bool { + return s.Style&Bold == Bold +} +func (s *StyledText) Faint() bool { + return s.Style&Faint == Faint +} +func (s *StyledText) Italic() bool { + return s.Style&Italic == Italic +} +func (s *StyledText) Blinking() bool { + return s.Style&Blinking == Blinking +} +func (s *StyledText) Inversed() bool { + return s.Style&Inversed == Inversed +} +func (s *StyledText) Invisible() bool { + return s.Style&Invisible == Invisible +} +func (s *StyledText) Underlined() bool { + return s.Style&Underlined == Underlined +} +func (s *StyledText) Strikethrough() bool { + return s.Style&Strikethrough == Strikethrough +} + +var ansiColorMap = map[string]map[string]*Col{ + "Normal": { + "30": Cols[0], + "31": Cols[1], + "32": Cols[2], + "33": Cols[3], + "34": Cols[4], + "35": Cols[5], + "36": Cols[6], + "37": Cols[7], + }, + "Bold": { + "30": Cols[8], + "31": Cols[9], + "32": Cols[10], + "33": Cols[11], + "34": Cols[12], + "35": Cols[13], + "36": Cols[14], + "37": Cols[15], + }, + "Faint": { + "30": Cols[0], + "31": Cols[1], + "32": Cols[2], + "33": Cols[3], + "34": Cols[4], + "35": Cols[5], + "36": Cols[6], + "37": Cols[7], + }, +} + +func ParseANSI(input string) ([]*StyledText, error) { + var result []*StyledText + invalid := fmt.Errorf("invalid ansi string") + missingTerminator := fmt.Errorf("missing escape terminator 'm'") + invalidTrueColorSequence := fmt.Errorf("invalid TrueColor sequence") + invalid256ColSequence := fmt.Errorf("invalid 256 colour sequence") + index := 0 + var currentStyledText *StyledText = &StyledText{} + + if len(input) == 0 { + return nil, invalid + } + + for { + // Read all chars to next escape code + esc := strings.Index(input, "\033[") + + // If no more esc chars, save what's left and return + if esc == -1 { + text := input[index:] + if len(text) > 0 { + currentStyledText.Label = text + result = append(result, currentStyledText) + } + return result, nil + } + label := input[:esc] + if len(label) > 0 { + currentStyledText.Label = label + result = append(result, currentStyledText) + currentStyledText = &StyledText{ + Label: "", + FgCol: currentStyledText.FgCol, + BgCol: currentStyledText.BgCol, + Style: currentStyledText.Style, + } + } + input = input[esc:] + // skip + input = input[2:] + + // Read in params + endesc := strings.Index(input, "m") + if endesc == -1 { + return nil, missingTerminator + } + paramText := input[:endesc] + input = input[endesc+1:] + params := strings.Split(paramText, ";") + colourMap := ansiColorMap["Normal"] + skip := 0 + for index, param := range params { + if skip > 0 { + skip-- + continue + } + switch param { + case "0": + // Reset styles + if len(params) == 1 { + if len(currentStyledText.Label) > 0 { + result = append(result, currentStyledText) + currentStyledText = &StyledText{ + Label: "", + FgCol: currentStyledText.FgCol, + BgCol: currentStyledText.BgCol, + Style: currentStyledText.Style, + } + continue + } + } + currentStyledText.Style = 0 + currentStyledText.FgCol = nil + currentStyledText.BgCol = nil + case "1": + // Bold + colourMap = ansiColorMap["Bold"] + currentStyledText.Style |= Bold + case "2": + // Dim/Feint + colourMap = ansiColorMap["Faint"] + currentStyledText.Style |= Faint + case "3": + // Italic + currentStyledText.Style |= Italic + case "4": + // Underlined + currentStyledText.Style |= Underlined + case "5": + // Blinking + currentStyledText.Style |= Blinking + case "7": + // Inverse + currentStyledText.Style |= Inversed + case "8": + // Invisible + currentStyledText.Style |= Invisible + case "9": + // Strikethrough + currentStyledText.Style |= Strikethrough + case "30", "31", "32", "33", "34", "35", "36", "37": + currentStyledText.FgCol = colourMap[param] + case "40", "41", "42", "43", "44", "45", "46", "47": + currentStyledText.BgCol = colourMap[param] + case "38", "48": + if len(params)-index < 2 { + return nil, invalid + } + // 256 colours + if params[index+1] == "5" { + skip = 2 + colIndexText := params[index+2] + colIndex, err := strconv.Atoi(colIndexText) + if err != nil { + return nil, invalid256ColSequence + } + if colIndex < 0 || colIndex > 255 { + return nil, invalid256ColSequence + } + if param == "38" { + currentStyledText.FgCol = Cols[colIndex] + continue + } + currentStyledText.BgCol = Cols[colIndex] + continue + } + // we must have 4 params left + if len(params)-index < 4 { + return nil, invalidTrueColorSequence + } + if params[index+1] != "2" { + return nil, invalidTrueColorSequence + } + var r, g, b uint8 + ri, err := strconv.Atoi(params[index+2]) + if err != nil { + return nil, invalidTrueColorSequence + } + gi, err := strconv.Atoi(params[index+3]) + if err != nil { + return nil, invalidTrueColorSequence + } + bi, err := strconv.Atoi(params[index+4]) + if err != nil { + return nil, invalidTrueColorSequence + } + if bi > 255 || gi > 255 || ri > 255 { + return nil, invalidTrueColorSequence + } + if bi < 0 || gi < 0 || ri < 0 { + return nil, invalidTrueColorSequence + } + r = uint8(ri) + g = uint8(gi) + b = uint8(bi) + skip = 4 + colvalue := fmt.Sprintf("#%02x%02x%02x", r, g, b) + if param == "38" { + currentStyledText.FgCol = &Col{Hex: colvalue, Rgb: Rgb{r, g, b}} + continue + } + currentStyledText.BgCol = &Col{Hex: colvalue} + default: + return nil, invalid + } + } + } + + return result, nil +} diff --git a/v2/pkg/menu/styledlabel_test.go b/v2/pkg/menu/styledlabel_test.go new file mode 100644 index 000000000..204190b7e --- /dev/null +++ b/v2/pkg/menu/styledlabel_test.go @@ -0,0 +1,197 @@ +package menu + +import ( + "testing" + + "github.com/matryer/is" +) + +func TestParseAnsi16SingleColour(t *testing.T) { + is := is.New(t) + tests := []struct { + name string + input string + wantText string + wantColor string + wantErr bool + }{ + {"No formatting", "Hello World", "Hello World", "", false}, + {"Black", "\u001b[0;30mHello World\033[0m", "Hello World", "Black", false}, + {"Red", "\u001b[0;31mHello World\033[0m", "Hello World", "Maroon", false}, + {"Green", "\u001b[0;32m\033[0m", "", "Green", false}, + {"Yellow", "\u001b[0;33m😀\033[0m", "😀", "Olive", false}, + {"Blue", "\u001b[0;34m123\033[0m", "123", "Navy", false}, + {"Purple", "\u001b[0;35m👩🏽‍🔧\u001B[0m", "👩🏽‍🔧", "Purple", false}, + {"Cyan", "\033[0;36m😀\033[0m", "😀", "Teal", false}, + {"White", "\u001b[0;37m[0;37m\033[0m", "[0;37m", "Silver", false}, + {"Black Bold", "\u001b[1;30mHello World\033[0m", "Hello World", "Grey", false}, + {"Red Bold", "\u001b[1;31mHello World\033[0m", "Hello World", "Red", false}, + {"Green Bold", "\u001b[1;32m\033[0m", "", "Lime", false}, + {"Yellow Bold", "\u001b[1;33m😀\033[0m", "😀", "Yellow", false}, + {"Blue Bold", "\u001b[1;34m123\033[0m", "123", "Blue", false}, + {"Purple Bold", "\u001b[1;35m👩🏽‍🔧\u001B[0m", "👩🏽‍🔧", "Fuchsia", false}, + {"Cyan Bold", "\033[1;36m😀\033[0m", "😀", "Aqua", false}, + {"White Bold", "\u001b[1;37m[0;37m\033[0m", "[0;37m", "White", false}, + {"Blank", "", "", "", true}, + {"Emoji", "😀👩🏽‍🔧", "😀👩🏽‍🔧", "", false}, + {"Spaces", " ", " ", "", false}, + {"Bad code", "\u001b[1 ", "", "", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseANSI(tt.input) + is.Equal(err != nil, tt.wantErr) + expectedLength := 1 + if tt.wantErr { + expectedLength = 0 + } + is.Equal(len(got), expectedLength) + if expectedLength == 1 { + if len(tt.wantColor) > 0 { + is.True(got[0].FgCol != nil) + is.Equal(got[0].FgCol.Name, tt.wantColor) + } + } + }) + } +} + +func TestParseAnsi16MultiColour(t *testing.T) { + is := is.New(t) + tests := []struct { + name string + input string + want []*StyledText + wantErr bool + }{ + {"Black & Red", "\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{ + {Label: "Hello World", FgCol: &Col{Name: "Black"}}, + {Label: "Hello World", FgCol: &Col{Name: "Maroon"}}, + }, false}, + {"Text then Black & Red", "This is great!\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{ + {Label: "This is great!"}, + {Label: "Hello World", FgCol: &Col{Name: "Black"}}, + {Label: "Hello World", FgCol: &Col{Name: "Maroon"}}, + }, false}, + {"Text Reset then Black & Red", "This is great!\u001B[0m\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{ + {Label: "This is great!"}, + {Label: "Hello World", FgCol: &Col{Name: "Black"}}, + {Label: "Hello World", FgCol: &Col{Name: "Maroon"}}, + }, false}, + {"Black & Red no reset", "\u001B[0;30mHello World\u001B[0;31mHello World", []*StyledText{ + {Label: "Hello World", FgCol: &Col{Name: "Black"}}, + {Label: "Hello World", FgCol: &Col{Name: "Maroon"}}, + }, false}, + {"Black,space,Red", "\u001B[0;30mHello World\u001B[0m \u001B[0;31mHello World\u001B[0m", []*StyledText{ + {Label: "Hello World", FgCol: &Col{Name: "Black"}}, + {Label: " "}, + {Label: "Hello World", FgCol: &Col{Name: "Maroon"}}, + }, false}, + {"Black,Red,Blue,Green underlined", "\033[4;30mBlack\u001B[0m\u001B[4;31mRed\u001B[0m\u001B[4;34mBlue\u001B[0m\u001B[4;32mGreen\u001B[0m", []*StyledText{ + {Label: "Black", FgCol: &Col{Name: "Black"}, Style: Underlined}, + {Label: "Red", FgCol: &Col{Name: "Maroon"}, Style: Underlined}, + {Label: "Blue", FgCol: &Col{Name: "Navy"}, Style: Underlined}, + {Label: "Green", FgCol: &Col{Name: "Green"}, Style: Underlined}, + }, false}, + {"Black,Red,Blue,Green bold", "\033[1;30mBlack\u001B[0m\u001B[1;31mRed\u001B[0m\u001B[1;34mBlue\u001B[0m\u001B[1;32mGreen\u001B[0m", []*StyledText{ + {Label: "Black", FgCol: &Col{Name: "Grey"}, Style: Bold}, + {Label: "Red", FgCol: &Col{Name: "Red"}, Style: Bold}, + {Label: "Blue", FgCol: &Col{Name: "Blue"}, Style: Bold}, + {Label: "Green", FgCol: &Col{Name: "Lime"}, Style: Bold}, + }, false}, + {"Green Feint & Yellow Italic", "\u001B[2;32m👩🏽‍🔧\u001B[0m\u001B[0;3;33m👩🏽‍🔧\u001B[0m", []*StyledText{ + {Label: "👩🏽‍🔧", FgCol: &Col{Name: "Green"}, Style: Faint}, + {Label: "👩🏽‍🔧", FgCol: &Col{Name: "Olive"}, Style: Italic}, + }, false}, + {"Green Blinking & Yellow Inversed", "\u001B[5;32m👩🏽‍🔧\u001B[0m\u001B[0;7;33m👩🏽‍🔧\u001B[0m", []*StyledText{ + {Label: "👩🏽‍🔧", FgCol: &Col{Name: "Green"}, Style: Blinking}, + {Label: "👩🏽‍🔧", FgCol: &Col{Name: "Olive"}, Style: Inversed}, + }, false}, + {"Green Invisible & Yellow Invisible & Strikethrough", "\u001B[8;32m👩🏽‍🔧\u001B[0m\u001B[9;33m👩🏽‍🔧\u001B[0m", []*StyledText{ + {Label: "👩🏽‍🔧", FgCol: &Col{Name: "Green"}, Style: Invisible}, + {Label: "👩🏽‍🔧", FgCol: &Col{Name: "Olive"}, Style: Invisible | Strikethrough}, + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseANSI(tt.input) + is.Equal(err != nil, tt.wantErr) + for index, w := range tt.want { + is.Equal(got[index].Label, w.Label) + if w.FgCol != nil { + is.Equal(got[index].FgCol.Name, w.FgCol.Name) + } + is.Equal(got[index].Style, w.Style) + } + }) + } +} + +func TestParseAnsi256(t *testing.T) { + is := is.New(t) + tests := []struct { + name string + input string + want []*StyledText + wantErr bool + }{ + {"Grey93 & DarkViolet", "\u001B[38;5;255mGrey93\u001B[0m\u001B[38;5;128mDarkViolet\u001B[0m", []*StyledText{ + {Label: "Grey93", FgCol: &Col{Name: "Grey93"}}, + {Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}}, + }, false}, + {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;255mGrey93\u001B[0m\u001B[0;3;38;5;128mDarkViolet\u001B[0m", []*StyledText{ + {Label: "Grey93", FgCol: &Col{Name: "Grey93"}, Style: Bold}, + {Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}, Style: Italic}, + }, false}, + {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;256mGrey93\u001B[0m", nil, true}, + {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;-1mGrey93\u001B[0m", nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseANSI(tt.input) + is.Equal(err != nil, tt.wantErr) + for index, w := range tt.want { + is.Equal(got[index].Label, w.Label) + if w.FgCol != nil { + is.Equal(got[index].FgCol.Name, w.FgCol.Name) + } + is.Equal(got[index].Style, w.Style) + } + }) + } +} + +func TestParseAnsiTrueColor(t *testing.T) { + is := is.New(t) + tests := []struct { + name string + input string + want []*StyledText + wantErr bool + }{ + {"Red", "\u001B[38;2;255;0;0mRed\u001B[0m", []*StyledText{ + {Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}}, + }, false}, + {"Red, text, Green", "\u001B[38;2;255;0;0mRed\u001B[0mI am plain text\u001B[38;2;0;255;0mGreen\u001B[0m", []*StyledText{ + {Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}}, + {Label: "I am plain text"}, + {Label: "Green", FgCol: &Col{Rgb: Rgb{0, 255, 0}, Hex: "#00ff00"}}, + }, false}, + {"Bad 1", "\u001B[38;2;256;0;0mRed\u001B[0m", nil, true}, + {"Bad 2", "\u001B[38;2;-1;0;0mRed\u001B[0m", nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseANSI(tt.input) + is.Equal(err != nil, tt.wantErr) + for index, w := range tt.want { + is.Equal(got[index].Label, w.Label) + if w.FgCol != nil { + is.Equal(got[index].FgCol.Hex, w.FgCol.Hex) + is.Equal(got[index].FgCol.Rgb, w.FgCol.Rgb) + } + is.Equal(got[index].Style, w.Style) + } + }) + } +} diff --git a/v2/pkg/menu/tray.go b/v2/pkg/menu/tray.go new file mode 100644 index 000000000..7554795ad --- /dev/null +++ b/v2/pkg/menu/tray.go @@ -0,0 +1,43 @@ +package menu + +// TrayMenu are the options +type TrayMenu struct { + + // Label is the text we wish to display in the tray + Label string + + // Image is the name of the tray icon we wish to display. + // These are read up during build from /trayicons and + // the filenames are used as IDs, minus the extension + // EG: /trayicons/main.png can be referenced here with "main" + // If the image is not a filename, it will be treated as base64 image data + Image string + + // MacTemplateImage indicates that on a Mac, this image is a template image + MacTemplateImage bool + + // Text Colour + RGBA string + + // Font + FontSize int + FontName string + + // Tooltip + Tooltip string + + // Callback function when menu clicked + //Click Callback `json:"-"` + + // Disabled makes the item unselectable + Disabled bool + + // Menu is the initial menu we wish to use for the tray + Menu *Menu + + // OnOpen is called when the Menu is opened + OnOpen func() + + // OnClose is called when the Menu is closed + OnClose func() +} diff --git a/v2/pkg/menu/type.go b/v2/pkg/menu/type.go new file mode 100644 index 000000000..829ea05ad --- /dev/null +++ b/v2/pkg/menu/type.go @@ -0,0 +1,17 @@ +package menu + +// Type of the menu item +type Type string + +const ( + // TextType is the text menuitem type + TextType Type = "Text" + // SeparatorType is the Separator menuitem type + SeparatorType Type = "Separator" + // SubmenuType is the Submenu menuitem type + SubmenuType Type = "Submenu" + // CheckboxType is the Checkbox menuitem type + CheckboxType Type = "Checkbox" + // RadioType is the Radio menuitem type + RadioType Type = "Radio" +) diff --git a/v2/pkg/menu/windows.go b/v2/pkg/menu/windows.go new file mode 100644 index 000000000..d251e287e --- /dev/null +++ b/v2/pkg/menu/windows.go @@ -0,0 +1,13 @@ +package menu + +/* +// DefaultWindowsMenu returns a default menu including the default +// Application and Edit menus. Use `.Append()` to add to it. +func DefaultWindowsMenu() *Menu { + return NewMenuFromItems( + FileMenu(), + EditMenu(), + WindowMenu(), + ) +} +*/ diff --git a/v2/pkg/options/default.go b/v2/pkg/options/default.go new file mode 100644 index 000000000..df3f3d52c --- /dev/null +++ b/v2/pkg/options/default.go @@ -0,0 +1,13 @@ +package options + +import ( + "github.com/wailsapp/wails/v2/pkg/logger" +) + +// Default options for creating the App +var Default = &App{ + Width: 1024, + Height: 768, + Logger: logger.NewDefaultLogger(), + LogLevel: logger.INFO, +} diff --git a/v2/pkg/options/mac/appearance.go b/v2/pkg/options/mac/appearance.go new file mode 100644 index 000000000..934bb208a --- /dev/null +++ b/v2/pkg/options/mac/appearance.go @@ -0,0 +1,23 @@ +package mac + +// AppearanceType is a type of Appearance for Cocoa windows +type AppearanceType string + +const ( + // DefaultAppearance uses the default system value + DefaultAppearance AppearanceType = "" + // NSAppearanceNameAqua - The standard light system appearance. + NSAppearanceNameAqua AppearanceType = "NSAppearanceNameAqua" + // NSAppearanceNameDarkAqua - The standard dark system appearance. + NSAppearanceNameDarkAqua AppearanceType = "NSAppearanceNameDarkAqua" + // NSAppearanceNameVibrantLight - The light vibrant appearance + NSAppearanceNameVibrantLight AppearanceType = "NSAppearanceNameVibrantLight" + // NSAppearanceNameAccessibilityHighContrastAqua - A high-contrast version of the standard light system appearance. + NSAppearanceNameAccessibilityHighContrastAqua AppearanceType = "NSAppearanceNameAccessibilityHighContrastAqua" + // NSAppearanceNameAccessibilityHighContrastDarkAqua - A high-contrast version of the standard dark system appearance. + NSAppearanceNameAccessibilityHighContrastDarkAqua AppearanceType = "NSAppearanceNameAccessibilityHighContrastDarkAqua" + // NSAppearanceNameAccessibilityHighContrastVibrantLight - A high-contrast version of the light vibrant appearance. + NSAppearanceNameAccessibilityHighContrastVibrantLight AppearanceType = "NSAppearanceNameAccessibilityHighContrastVibrantLight" + // NSAppearanceNameAccessibilityHighContrastVibrantDark - A high-contrast version of the dark vibrant appearance. + NSAppearanceNameAccessibilityHighContrastVibrantDark AppearanceType = "NSAppearanceNameAccessibilityHighContrastVibrantDark" +) diff --git a/v2/pkg/options/mac/mac.go b/v2/pkg/options/mac/mac.go new file mode 100644 index 000000000..63e4e6194 --- /dev/null +++ b/v2/pkg/options/mac/mac.go @@ -0,0 +1,19 @@ +package mac + +type ActivationPolicy int + +const ( + NSApplicationActivationPolicyRegular ActivationPolicy = 0 + NSApplicationActivationPolicyAccessory ActivationPolicy = 1 + NSApplicationActivationPolicyProhibited ActivationPolicy = 2 +) + +// Options are options specific to Mac +type Options struct { + TitleBar *TitleBar + Appearance AppearanceType + WebviewIsTransparent bool + WindowIsTranslucent bool + ActivationPolicy ActivationPolicy + //URLHandlers map[string]func(string) +} diff --git a/v2/pkg/options/mac/titlebar.go b/v2/pkg/options/mac/titlebar.go new file mode 100644 index 000000000..c18c4eea8 --- /dev/null +++ b/v2/pkg/options/mac/titlebar.go @@ -0,0 +1,54 @@ +package mac + +// TitleBar contains options for the Mac titlebar +type TitleBar struct { + TitlebarAppearsTransparent bool + HideTitle bool + HideTitleBar bool + FullSizeContent bool + UseToolbar bool + HideToolbarSeparator bool +} + +// TitleBarDefault results in the default Mac Titlebar +func TitleBarDefault() *TitleBar { + return &TitleBar{ + TitlebarAppearsTransparent: false, + HideTitle: false, + HideTitleBar: false, + FullSizeContent: false, + UseToolbar: false, + HideToolbarSeparator: false, + } +} + +// Credit: Comments from Electron site + +// TitleBarHidden results in a hidden title bar and a full size content window, +// yet the title bar still has the standard window controls (“traffic lights”) +// in the top left. +func TitleBarHidden() *TitleBar { + return &TitleBar{ + TitlebarAppearsTransparent: true, + HideTitle: true, + HideTitleBar: false, + FullSizeContent: true, + UseToolbar: false, + HideToolbarSeparator: false, + } +} + +// TitleBarHiddenInset results in a hidden title bar with an alternative look where +// the traffic light buttons are slightly more inset from the window edge. +func TitleBarHiddenInset() *TitleBar { + + return &TitleBar{ + TitlebarAppearsTransparent: true, + HideTitle: true, + HideTitleBar: false, + FullSizeContent: true, + UseToolbar: true, + HideToolbarSeparator: true, + } + +} diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go new file mode 100644 index 000000000..61c800d52 --- /dev/null +++ b/v2/pkg/options/options.go @@ -0,0 +1,97 @@ +package options + +import ( + "context" + "embed" + "log" + + "github.com/wailsapp/wails/v2/pkg/options/mac" + "github.com/wailsapp/wails/v2/pkg/options/windows" + + "github.com/wailsapp/wails/v2/pkg/menu" + + "github.com/imdario/mergo" + "github.com/wailsapp/wails/v2/pkg/logger" +) + +// App contains options for creating the App +type App struct { + Title string + Width int + Height int + DisableResize bool + Fullscreen bool + Frameless bool + MinWidth int + MinHeight int + MaxWidth int + MaxHeight int + StartHidden bool + HideWindowOnClose bool + AlwaysOnTop bool + RGBA *RGBA + Assets embed.FS + Menu *menu.Menu + Logger logger.Logger `json:"-"` + LogLevel logger.LogLevel + OnStartup func(ctx context.Context) `json:"-"` + OnDomReady func(ctx context.Context) `json:"-"` + OnShutdown func(ctx context.Context) `json:"-"` + Bind []interface{} + + //ContextMenus []*menu.ContextMenu + //TrayMenus []*menu.TrayMenu + Windows *windows.Options + Mac *mac.Options +} + +type RGBA struct { + R uint8 `json:"r"` + G uint8 `json:"g"` + B uint8 `json:"b"` + A uint8 `json:"a"` +} + +// MergeDefaults will set the minimum default values for an application +func MergeDefaults(appoptions *App) { + err := mergo.Merge(appoptions, Default) + if err != nil { + log.Fatal(err) + } + + // DEfault colour. Doesn't work well with mergo + if appoptions.RGBA == nil { + appoptions.RGBA = &RGBA{ + R: 255, + G: 255, + B: 255, + A: 255, + } + } + + // Ensure max and min are valid + if appoptions.MinWidth > 0 && appoptions.MaxWidth > 0 { + if appoptions.MinWidth > appoptions.MaxWidth { + appoptions.MinWidth = appoptions.MaxWidth + } + } + if appoptions.MinHeight > 0 && appoptions.MaxHeight > 0 { + if appoptions.MinHeight > appoptions.MaxHeight { + appoptions.MinHeight = appoptions.MaxHeight + } + } + // Ensure width and height are limited if max/min is set + if appoptions.Width < appoptions.MinWidth { + appoptions.Width = appoptions.MinWidth + } + if appoptions.MaxWidth > 0 && appoptions.Width > appoptions.MaxWidth { + appoptions.Width = appoptions.MaxWidth + } + if appoptions.Height < appoptions.MinHeight { + appoptions.Height = appoptions.MinHeight + } + if appoptions.MaxHeight > 0 && appoptions.Height > appoptions.MaxHeight { + appoptions.Height = appoptions.MaxHeight + } + +} diff --git a/v2/pkg/options/options_test.go b/v2/pkg/options/options_test.go new file mode 100644 index 000000000..5f75b5acc --- /dev/null +++ b/v2/pkg/options/options_test.go @@ -0,0 +1,85 @@ +package options + +import ( + "testing" +) + +func TestMergeDefaultsWH(t *testing.T) { + tests := []struct { + name string + appoptions *App + wantWidth int + wantHeight int + }{ + { + name: "No width and height", + appoptions: &App{}, + wantWidth: Default.Width, + wantHeight: Default.Height, + }, + { + name: "Basic width and height", + appoptions: &App{ + Width: 800, + Height: 600, + }, + wantWidth: 800, + wantHeight: 600, + }, + { + name: "With MinWidth and MinHeight", + appoptions: &App{ + Width: 200, + MinWidth: 800, + Height: 100, + MinHeight: 600, + }, + wantWidth: 800, + wantHeight: 600, + }, + { + name: "With MaxWidth and MaxHeight", + appoptions: &App{ + Width: 900, + MaxWidth: 800, + Height: 700, + MaxHeight: 600, + }, + wantWidth: 800, + wantHeight: 600, + }, + { + name: "With MinWidth more than MaxWidth", + appoptions: &App{ + Width: 900, + MinWidth: 900, + MaxWidth: 800, + Height: 600, + }, + wantWidth: 800, + wantHeight: 600, + }, + { + name: "With MinHeight more than MaxHeight", + appoptions: &App{ + Width: 800, + Height: 700, + MinHeight: 900, + MaxHeight: 600, + }, + wantWidth: 800, + wantHeight: 600, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + MergeDefaults(tt.appoptions) + if tt.appoptions.Width != tt.wantWidth { + t.Errorf("MergeDefaults().Width =%v, want %v", tt.appoptions.Width, tt.wantWidth) + } + if tt.appoptions.Height != tt.wantHeight { + t.Errorf("MergeDefaults().Height =%v, want %v", tt.appoptions.Height, tt.wantHeight) + } + }) + } +} diff --git a/v2/pkg/options/windows/windows.go b/v2/pkg/options/windows/windows.go new file mode 100644 index 000000000..33c842b2a --- /dev/null +++ b/v2/pkg/options/windows/windows.go @@ -0,0 +1,8 @@ +package windows + +// Options are options specific to Windows +type Options struct { + WebviewIsTransparent bool + WindowIsTranslucent bool + DisableWindowIcon bool +} diff --git a/v2/pkg/parser/applicationVariableName.go b/v2/pkg/parser/applicationVariableName.go new file mode 100644 index 000000000..de4252933 --- /dev/null +++ b/v2/pkg/parser/applicationVariableName.go @@ -0,0 +1,50 @@ +package parser + +import "go/ast" + +func (p *Package) getApplicationVariableName(file *ast.File, wailsImportName string) string { + + // Iterate through the whole file looking for the application name + applicationVariableName := "" + + // Inspect the file + ast.Inspect(file, func(n ast.Node) bool { + // Parse Assignments looking for application name + if assignStmt, ok := n.(*ast.AssignStmt); ok { + + // Check the RHS is of the form: + // `app := wails.CreateApp()` or + // `app := wails.CreateAppWithOptions` + for _, rhs := range assignStmt.Rhs { + ce, ok := rhs.(*ast.CallExpr) + if !ok { + continue + } + se, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + i, ok := se.X.(*ast.Ident) + if !ok { + continue + } + // Have we found the wails import name? + if i.Name == wailsImportName { + // Check we are calling a function to create the app + if se.Sel.Name == "CreateApp" || se.Sel.Name == "CreateAppWithOptions" { + if len(assignStmt.Lhs) == 1 { + i, ok := assignStmt.Lhs[0].(*ast.Ident) + if ok { + // Found the app variable name + applicationVariableName = i.Name + return false + } + } + } + } + } + } + return true + }) + return applicationVariableName +} diff --git a/v2/pkg/parser/comments.go b/v2/pkg/parser/comments.go new file mode 100644 index 000000000..019e9880c --- /dev/null +++ b/v2/pkg/parser/comments.go @@ -0,0 +1,21 @@ +package parser + +import ( + "go/ast" + "strings" +) + +func parseComments(comments *ast.CommentGroup) []string { + var result []string + + if comments == nil { + return result + } + + for _, comment := range comments.List { + commentText := strings.TrimPrefix(comment.Text, "//") + result = append(result, commentText) + } + + return result +} diff --git a/v2/pkg/parser/conversion.go b/v2/pkg/parser/conversion.go new file mode 100644 index 000000000..99daa62f2 --- /dev/null +++ b/v2/pkg/parser/conversion.go @@ -0,0 +1,156 @@ +package parser + +import ( + "fmt" + "strings" + + "github.com/leaanthony/slicer" +) + +// JSType represents a javascript type +type JSType string + +const ( + // JsString is a JS string + JsString JSType = "string" + // JsBoolean is a JS bool + JsBoolean = "boolean" + // JsInt is a JS number + JsInt = "number" + // JsFloat is a JS number + JsFloat = "number" + // JsArray is a JS array + JsArray = "Array" + // JsObject is a JS object + JsObject = "Object" + // JsUnsupported represents a type that cannot be converted + JsUnsupported = "*" +) + +func goTypeToJS(input *Field) string { + switch input.Type { + case "string": + return "string" + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + return "number" + case "float32", "float64": + return "number" + case "bool": + return "boolean" + // case reflect.Array, reflect.Slice: + // return JsArray + // case reflect.Ptr, reflect.Struct, reflect.Map, reflect.Interface: + // return JsObject + case "struct": + return input.Struct.Name + default: + fmt.Printf("Unsupported input to goTypeToJS: %+v", input) + return "*" + } +} + +// goTypeToTS converts the given field into a Typescript type +// The pkgName is the package that the field is being output in. +// This is used to ensure we don't qualify local structs. +func goTypeToTS(input *Field, pkgName string) string { + var result string + switch input.Type { + case "string": + result = "string" + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + result = "number" + case "float32", "float64": + result = "number" + case "bool": + result = "boolean" + case "struct": + if input.Struct.Package.Name != "" { + if input.Struct.Package.Name != pkgName { + result = input.Struct.Package.Name + "." + } + } + result += input.Struct.Name + // case reflect.Array, reflect.Slice: + // return string(JsArray) + // case reflect.Ptr, reflect.Struct: + // fqt := input.Type().String() + // return strings.Split(fqt, ".")[1] + // case reflect.Map, reflect.Interface: + // return string(JsObject) + default: + fmt.Printf("Unsupported input to goTypeToTS: %+v", input) + return JsUnsupported + } + + if input.IsArray { + result = result + "[]" + } + + return result +} + +func goTypeToTSDeclaration(input *Field, pkgName string) string { + var result string + switch input.Type { + case "string": + result = "string" + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + result = "number" + case "float32", "float64": + result = "number" + case "bool": + result = "boolean" + case "struct": + if input.Struct.Package.Name != "" { + if input.Struct.Package.Name != pkgName { + result = `import("./_` + input.Struct.Package.Name + `").` + } + } + result += input.Struct.Name + // case reflect.Array, reflect.Slice: + // return string(JsArray) + // case reflect.Ptr, reflect.Struct: + // fqt := input.Type().String() + // return strings.Split(fqt, ".")[1] + // case reflect.Map, reflect.Interface: + // return string(JsObject) + default: + fmt.Printf("Unsupported input to goTypeToTS: %+v", input) + return JsUnsupported + } + + if input.IsArray { + result = result + "[]" + } + + return result +} + +func isUnresolvedType(typeName string) bool { + switch typeName { + case "string": + return false + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + return false + case "float32", "float64": + return false + case "bool": + return false + case "struct": + return false + default: + return true + } +} + +var reservedJSWords []string = []string{"abstract", "arguments", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "eval", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield", "Array", "Date", "eval", "function", "hasOwnProperty", "Infinity", "isFinite", "isNaN", "isPrototypeOf", "length", "Math", "NaN", "Number", "Object", "prototype", "String", "toString", "undefined", "valueOf"} +var jsReservedWords *slicer.StringSlicer = slicer.String(reservedJSWords) + +func isJSReservedWord(input string) bool { + return jsReservedWords.Contains(input) +} + +func startsWithLowerCaseLetter(input string) bool { + firstLetter := string(input[0]) + return strings.ToLower(firstLetter) == firstLetter +} diff --git a/v2/pkg/parser/field.go b/v2/pkg/parser/field.go new file mode 100644 index 000000000..bcdc76427 --- /dev/null +++ b/v2/pkg/parser/field.go @@ -0,0 +1,309 @@ +package parser + +import ( + "fmt" + "go/ast" + "strings" + + "github.com/fatih/structtag" +) + +// Field defines a parsed struct field +type Field struct { + + // Name of the field + Name string + + // The type of the field. + // "struct" if it's a struct + Type string + + // A pointer to the struct if the Type is "struct" + Struct *Struct + + // User comments on the field + Comments []string + + // Indicates if the Field is an array of type "Type" + IsArray bool + + // JSON field name defined by a json tag + JSONOptions +} + +type JSONOptions struct { + Name string + IsOptional bool + Ignored bool +} + +// JSType returns the Javascript type for this field +func (f *Field) JSType() string { + return string(goTypeToJS(f)) +} + +// JSName returns the Javascript name for this field +func (f *Field) JSName() string { + if f.JSONOptions.Name != "" { + return f.JSONOptions.Name + } + return f.Name +} + +// TSName returns the Typescript name for this field +func (f *Field) TSName() string { + result := f.Name + if f.JSONOptions.Name != "" { + result = f.JSONOptions.Name + } + if f.IsOptional { + result += "?" + } + return result +} + +// AsTSDeclaration returns a TS definition of a single type field +func (f *Field) AsTSDeclaration(pkgName string) string { + return f.TSName() + ": " + f.TypeAsTSType(pkgName) +} + +// NameForPropertyDoc returns a formatted name for the jsdoc @property declaration +func (f *Field) NameForPropertyDoc() string { + if f.IsOptional { + return "[" + f.JSName() + "]" + } + return f.JSName() +} + +// TypeForPropertyDoc returns a formatted name for the jsdoc @property declaration +func (f *Field) TypeForPropertyDoc() string { + result := goTypeToJS(f) + if f.IsArray { + result += "[]" + } + return result +} + +// TypeAsTSType converts the Field type to something TS wants +func (f *Field) TypeAsTSType(pkgName string) string { + var result = "" + switch f.Type { + case "string": + result = "string" + case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": + result = "number" + case "float32", "float64": + result = "number" + case "bool": + result = "boolean" + case "struct": + if f.Struct.Package != nil { + if f.Struct.Package.Name != pkgName { + result = f.Struct.Package.Name + "." + } + } + result = result + f.Struct.Name + default: + result = "any" + } + + return result +} + +func (p *Parser) parseField(file *ast.File, field *ast.Field, pkg *Package) ([]*Field, error) { + var result []*Field + + var fieldType string + var strct *Struct + var isArray bool + + var jsonOptions JSONOptions + + // Determine type + switch t := field.Type.(type) { + case *ast.Ident: + fieldType = t.Name + + unresolved := isUnresolvedType(fieldType) + + // Check if this type is actually a struct + if unresolved { + // Assume it is a struct + // Parse the struct + var err error + strct, err = p.parseStruct(pkg, t.Name) + if err != nil { + return nil, err + } + + if strct == nil { + fieldName := "" + if len(field.Names) > 0 { + fieldName = field.Names[0].Name + } + return nil, fmt.Errorf("unresolved type in field %s: %s", fieldName, fieldType) + } + + fieldType = "struct" + + } + case *ast.StarExpr: + fieldType = "struct" + packageName, structName, err := parseStructNameFromStarExpr(t) + if err != nil { + return nil, err + } + + // If this is an external package, find it + if packageName != "" { + referencedGoPackage := pkg.getImportByName(packageName, file) + referencedPackage := p.getPackageByID(referencedGoPackage.ID) + + // If we found the struct, save it as an external package reference + if referencedPackage != nil { + pkg.addExternalReference(referencedPackage) + } + + // We save this to pkg anyway, because we want to know if this package + // was NOT found + pkg = referencedPackage + } + + // If this is a package in our project, parse the struct! + if pkg != nil { + + // Parse the struct + strct, err = p.parseStruct(pkg, structName) + if err != nil { + return nil, err + } + + } + + case *ast.ArrayType: + isArray = true + // Parse the Elt (There must be a better way!) + switch t := t.Elt.(type) { + case *ast.Ident: + fieldType = t.Name + case *ast.StarExpr: + fieldType = "struct" + packageName, structName, err := parseStructNameFromStarExpr(t) + if err != nil { + return nil, err + } + + // If this is an external package, find it + if packageName != "" { + referencedGoPackage := pkg.getImportByName(packageName, file) + referencedPackage := p.getPackageByID(referencedGoPackage.ID) + + // If we found the struct, save it as an external package reference + if referencedPackage != nil { + pkg.addExternalReference(referencedPackage) + } + + // We save this to pkg anyway, because we want to know if this package + // was NOT found + pkg = referencedPackage + } + + // If this is a package in our project, parse the struct! + if pkg != nil { + + // Parse the struct + strct, err = p.parseStruct(pkg, structName) + if err != nil { + return nil, err + } + + } + default: + // We will default to "Array" for eg nested arrays + fieldType = "any" + } + + default: + return nil, fmt.Errorf("unsupported field found in struct: %+v", t) + } + + // Parse json tag if available + if field.Tag != nil { + err := parseJSONOptions(field.Tag.Value, &jsonOptions) + if err != nil { + return nil, err + } + } + + // Loop over names if we have + if len(field.Names) > 0 { + + for _, name := range field.Names { + + // TODO: Check field names are valid in JS + if isJSReservedWord(name.Name) { + return nil, fmt.Errorf("unable to use field name %s - reserved word in Javascript", name.Name) + } + + // Create a field per name + thisField := &Field{ + Comments: parseComments(field.Doc), + } + thisField.Name = name.Name + thisField.Type = fieldType + thisField.Struct = strct + thisField.IsArray = isArray + thisField.JSONOptions = jsonOptions + + result = append(result, thisField) + } + return result, nil + } + + // When we have no name + thisField := &Field{ + Comments: parseComments(field.Doc), + } + thisField.Type = fieldType + thisField.Struct = strct + thisField.IsArray = isArray + result = append(result, thisField) + + return result, nil +} + +func parseJSONOptions(fieldTag string, jsonOptions *JSONOptions) error { + + // Remove backticks + fieldTag = strings.Trim(fieldTag, "`") + + // Parse the tag + tags, err := structtag.Parse(fieldTag) + if err != nil { + return err + } + + jsonTag, err := tags.Get("json") + if err != nil { + return err + } + + if jsonTag == nil { + return nil + } + + // Save the name + jsonOptions.Name = jsonTag.Name + + // Check if this field is ignored + if jsonTag.Name == "-" { + jsonOptions.Ignored = true + } + + // Check if this field is optional + if jsonTag.HasOption("omitempty") { + jsonOptions.IsOptional = true + } + + return nil +} diff --git a/v2/pkg/parser/findBoundStructs.go b/v2/pkg/parser/findBoundStructs.go new file mode 100644 index 000000000..91876a8b2 --- /dev/null +++ b/v2/pkg/parser/findBoundStructs.go @@ -0,0 +1,152 @@ +package parser + +import ( + "fmt" + "go/ast" +) + +// findBoundStructs will search through the Wails project looking +// for which structs have been bound using the `Bind()` method +func (p *Parser) findBoundStructs(pkg *Package) error { + + // Iterate through the files in the package looking for the bound structs + for _, fileAst := range pkg.Gopackage.Syntax { + + // Find the wails import name + wailsImportName := pkg.getWailsImportName(fileAst) + + // If this file doesn't import wails, continue + if wailsImportName == "" { + continue + } + + applicationVariableName := pkg.getApplicationVariableName(fileAst, wailsImportName) + if applicationVariableName == "" { + continue + } + + var parseError error + + ast.Inspect(fileAst, func(n ast.Node) bool { + // Parse Call expressions looking for bind calls + callExpr, ok := n.(*ast.CallExpr) + if !ok { + return true + } + // Check this is the right kind of expression (something.something()) + f, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + ident, ok := f.X.(*ast.Ident) + if !ok { + return true + } + + if ident.Name != applicationVariableName { + return true + } + + if f.Sel.Name != "Bind" { + return true + } + + if len(callExpr.Args) != 1 { + return true + } + + // Work out what was bound + switch boundItem := callExpr.Args[0].(type) { + + // app.Bind( someFunction() ) + case *ast.CallExpr: + switch fn := boundItem.Fun.(type) { + case *ast.Ident: + // boundStructs = append(boundStructs, newStruct(pkg.Name, fn.Name)) + strct, err := p.getFunctionReturnType(pkg, fn.Name) + if err != nil { + parseError = err + return false + } + if strct == nil { + parseError = fmt.Errorf("unable to resolve function returntype: %s", fn.Name) + return false + } + strct.Package.boundStructs.Add(strct.Name) + case *ast.SelectorExpr: + ident, ok := fn.X.(*ast.Ident) + if !ok { + return true + } + packageName := ident.Name + functionName := fn.Sel.Name + println("Found bound function:", packageName+"."+functionName) + + // Get package for package name + externalPackageName := pkg.getImportByName(packageName, fileAst) + externalPackage := p.getPackageByID(externalPackageName.ID) + + strct, err := p.getFunctionReturnType(externalPackage, functionName) + if err != nil { + parseError = err + return false + } + if strct == nil { + // Unable to resolve function + return true + } + externalPackage.boundStructs.Add(strct.Name) + } + + // Binding struct pointer literals + case *ast.UnaryExpr: + + if boundItem.Op.String() != "&" { + return true + } + + cl, ok := boundItem.X.(*ast.CompositeLit) + if !ok { + return true + } + + switch boundStructExp := cl.Type.(type) { + + // app.Bind( &myStruct{} ) + case *ast.Ident: + pkg.boundStructs.Add(boundStructExp.Name) + + // app.Bind( &mypackage.myStruct{} ) + case *ast.SelectorExpr: + var structName = "" + var packageName = "" + switch x := boundStructExp.X.(type) { + case *ast.Ident: + packageName = x.Name + default: + // TODO: Save these warnings + // println("Identifier in binding not supported:") + return true + } + structName = boundStructExp.Sel.Name + referencedPackage := pkg.getImportByName(packageName, fileAst) + packageWrapper := p.getPackageByID(referencedPackage.ID) + packageWrapper.boundStructs.Add(structName) + } + + default: + // TODO: Save these warnings + // println("Unsupported bind expression:") + // spew.Dump(boundItem) + } + + return true + }) + + if parseError != nil { + return parseError + } + } + + return nil +} diff --git a/v2/pkg/parser/generate.go b/v2/pkg/parser/generate.go new file mode 100644 index 000000000..b45a524c5 --- /dev/null +++ b/v2/pkg/parser/generate.go @@ -0,0 +1,251 @@ +package parser + +import ( + "bytes" + _ "embed" + "io/ioutil" + "os" + "path/filepath" + "text/template" + + "github.com/pkg/errors" + "github.com/wailsapp/wails/v2/internal/fs" +) + +//go:embed index.template +var indexTemplate string + +//go:embed index.d.template +var indexDTemplate string + +//go:embed package.template +var packageTemplate string + +//go:embed package.d.template +var packageDTemplate string + +//go:embed globals.d.template +var globalsDTemplate string + +//go:embed package.json +var packageJSON string + +// GenerateWailsFrontendPackage will generate a Javascript/Typescript +// package in `/frontend/wails` that defines which methods +// and structs are bound to your frontend +func GenerateWailsFrontendPackage() (*ParserReport, error) { + + dir, err := os.Getwd() + if err != nil { + return nil, err + } + + p := NewParser() + + err = p.ParseProject(dir) + if err != nil { + return nil, err + } + + err = p.generateModule() + + return p.parserReport(), err +} + +func (p *Parser) generateModule() error { + + moduleDir, err := createBackendJSDirectory() + if err != nil { + return err + } + + packagesToGenerate := p.packagesToGenerate() + + for _, pkg := range packagesToGenerate { + + err := generatePackage(pkg, moduleDir) + if err != nil { + return err + } + } + + // Copy the standard files + tgtFile := filepath.Join(moduleDir, "package.json") + err = fs.CopyFile(packageJSON, tgtFile) + if err != nil { + return err + } + + // Generate the globals.d.ts file + err = generateGlobalsTS(moduleDir, packagesToGenerate) + if err != nil { + return err + } + + // Generate the index.js file + err = generateIndexJS(moduleDir, packagesToGenerate) + if err != nil { + return err + } + // Generate the index.d.ts file + err = generateIndexTS(moduleDir, packagesToGenerate) + if err != nil { + return err + } + + return nil +} + +func createBackendJSDirectory() (string, error) { + + // Calculate the package directory + // Note this is *always* called from the project directory + // so using paths relative to CWD is fine + dir, err := fs.RelativeToCwd("./frontend/backend") + if err != nil { + return "", errors.Wrap(err, "Error creating backend module directory") + } + + // Remove directory if it exists - REGENERATION! + err = os.RemoveAll(dir) + if err != nil { + return "", errors.Wrap(err, "Error removing module directory") + } + + // Make the directory + err = fs.Mkdir(dir) + + return dir, err +} + +func generatePackage(pkg *Package, moduledir string) error { + + // Load typescript template + typescriptTemplateData := fs.MustLoadString(packageDTemplate) + typescriptTemplate, err := template.New("typescript").Parse(typescriptTemplateData) + if err != nil { + return errors.Wrap(err, "Error creating template") + } + + // Execute javascript template + var buffer bytes.Buffer + err = typescriptTemplate.Execute(&buffer, pkg) + if err != nil { + return errors.Wrap(err, "Error generating code") + } + + // Save typescript file + err = ioutil.WriteFile(filepath.Join(moduledir, "_"+pkg.Name+".d.ts"), buffer.Bytes(), 0755) + if err != nil { + return errors.Wrap(err, "Error writing backend package file") + } + + // Load javascript template + javascriptTemplateData := fs.MustLoadString(packageTemplate) + javascriptTemplate, err := template.New("javascript").Parse(javascriptTemplateData) + if err != nil { + return errors.Wrap(err, "Error creating template") + } + + // Reset the buffer + buffer.Reset() + + err = javascriptTemplate.Execute(&buffer, pkg) + if err != nil { + return errors.Wrap(err, "Error generating code") + } + + // Save javascript file + err = ioutil.WriteFile(filepath.Join(moduledir, "_"+pkg.Name+".js"), buffer.Bytes(), 0755) + if err != nil { + return errors.Wrap(err, "Error writing backend package file") + } + + return nil +} + +func generateIndexJS(dir string, packages []*Package) error { + + // Load template + templateData := fs.MustLoadString(indexTemplate) + packagesTemplate, err := template.New("index").Parse(templateData) + if err != nil { + return errors.Wrap(err, "Error creating template") + } + + // Execute template + var buffer bytes.Buffer + err = packagesTemplate.Execute(&buffer, packages) + if err != nil { + return errors.Wrap(err, "Error generating code") + } + + // Calculate target filename + indexJS := filepath.Join(dir, "index.js") + + err = ioutil.WriteFile(indexJS, buffer.Bytes(), 0755) + if err != nil { + return errors.Wrap(err, "Error writing backend package index.js file") + } + + return nil +} +func generateIndexTS(dir string, packages []*Package) error { + + // Load template + templateData := fs.MustLoadString(indexDTemplate) + indexTSTemplate, err := template.New("index.d").Parse(templateData) + if err != nil { + return errors.Wrap(err, "Error creating template") + } + + // Execute template + var buffer bytes.Buffer + err = indexTSTemplate.Execute(&buffer, packages) + if err != nil { + return errors.Wrap(err, "Error generating code") + } + + // Calculate target filename + indexJS := filepath.Join(dir, "index.d.ts") + + err = ioutil.WriteFile(indexJS, buffer.Bytes(), 0755) + if err != nil { + return errors.Wrap(err, "Error writing backend package index.d.ts file") + } + + return nil +} + +func generateGlobalsTS(dir string, packages []*Package) error { + + // Load template + templateData := fs.MustLoadString(globalsDTemplate) + packagesTemplate, err := template.New("globals").Parse(templateData) + if err != nil { + return errors.Wrap(err, "Error creating template") + } + + // Execute template + var buffer bytes.Buffer + err = packagesTemplate.Execute(&buffer, packages) + if err != nil { + return errors.Wrap(err, "Error generating code") + } + + // Calculate target filename + indexJS := filepath.Join(dir, "globals.d.ts") + + err = ioutil.WriteFile(indexJS, buffer.Bytes(), 0755) + if err != nil { + return errors.Wrap(err, "Error writing backend package globals.d.ts file") + } + + return nil +} + +func (p *Parser) parserReport() *ParserReport { + return &ParserReport{ + Packages: p.packagesToGenerate(), + } +} diff --git a/v2/pkg/parser/getFunctionReturnType.go b/v2/pkg/parser/getFunctionReturnType.go new file mode 100644 index 000000000..f5f2c6cd9 --- /dev/null +++ b/v2/pkg/parser/getFunctionReturnType.go @@ -0,0 +1,69 @@ +package parser + +import ( + "fmt" + "go/ast" +) + +func (p *Parser) getFunctionReturnType(pkg *Package, functionName string) (*Struct, error) { + + var result *Struct + + // Iterate through the files in the package looking for the bound structs + for _, fileAst := range pkg.Gopackage.Syntax { + + var parseError error + + ast.Inspect(fileAst, func(n ast.Node) bool { + // Parse Call expressions looking for bind calls + funcDecl, ok := n.(*ast.FuncDecl) + if !ok { + return true + } + + if funcDecl.Name.Name == functionName { + result, parseError = p.parseFunctionReturnType(fileAst, funcDecl, pkg) + return false + } + + return true + }) + + if parseError != nil { + return nil, parseError + } + + if result != nil { + return result, nil + } + } + + return result, nil +} + +func (p *Parser) parseFunctionReturnType(file *ast.File, funcDecl *ast.FuncDecl, pkg *Package) (*Struct, error) { + + var result *Struct + + if funcDecl.Type.Results == nil { + return nil, fmt.Errorf("bound function %s has no return values", funcDecl.Name.Name) + } + + // We expect only 1 return value for a function return + if len(funcDecl.Type.Results.List) > 1 { + return nil, fmt.Errorf("bound function %s has more than 1 return value", funcDecl.Name.Name) + } + + parsedFields, err := p.parseField(file, funcDecl.Type.Results.List[0], pkg) + if err != nil { + return nil, err + } + + if len(parsedFields) > 1 { + return nil, fmt.Errorf("bound function %s has more than 1 return value", funcDecl.Name.Name) + } + + result = parsedFields[0].Struct + + return result, nil +} diff --git a/v2/pkg/parser/globals.d.template b/v2/pkg/parser/globals.d.template new file mode 100644 index 000000000..acf85a7ae --- /dev/null +++ b/v2/pkg/parser/globals.d.template @@ -0,0 +1,27 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +interface Window { + + go: { + + {{- range . }}{{$packageName:=.Name}} + {{- if .HasBoundStructs }} + {{ $packageName }}: { + {{- range .Structs }} + {{- if .IsBound }} + {{if .Comments }}{{range .Comments}}// {{ . }}{{end}}{{end}} + {{.Name}}: { + {{range .Methods}} + {{if .Comments }}{{range .Comments}}// {{ . }}{{end}}{{end}} + {{.Name}}: ({{.InputsAsTSText $packageName}}) => Promise<{{.OutputsAsTSText $packageName}}>, + {{end}} + } + {{- end}} + {{- end}} + } + {{- end}} + {{- end}} + } +} \ No newline at end of file diff --git a/v2/pkg/parser/index.d.template b/v2/pkg/parser/index.d.template new file mode 100644 index 000000000..f48417d09 --- /dev/null +++ b/v2/pkg/parser/index.d.template @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +{{- range .}} +export const {{.Name}}: typeof import("./_{{.Name}}"); +{{- end}} diff --git a/v2/pkg/parser/index.template b/v2/pkg/parser/index.template new file mode 100644 index 000000000..f1801e04e --- /dev/null +++ b/v2/pkg/parser/index.template @@ -0,0 +1,13 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +{{- range .}} +const {{.Name}} = require('./_{{.Name}}'); +{{- end}} + +module.exports = { + {{- range .}} + {{.Name}}: {{.Name}}, + {{- end}} +} \ No newline at end of file diff --git a/v2/pkg/parser/method.go b/v2/pkg/parser/method.go new file mode 100644 index 000000000..3ba3b10c1 --- /dev/null +++ b/v2/pkg/parser/method.go @@ -0,0 +1,174 @@ +package parser + +import ( + "fmt" + "go/ast" + "strings" +) + +// Method defines a struct method +type Method struct { + Name string + Comments []string + Inputs []*Field + Returns []*Field +} + +func (p *Parser) parseStructMethods(boundStruct *Struct) error { + + for _, fileAst := range boundStruct.Package.Gopackage.Syntax { + + // Track errors + var parseError error + + ast.Inspect(fileAst, func(n ast.Node) bool { + + if funcDecl, ok := n.(*ast.FuncDecl); ok { + + if funcDecl.Recv == nil { + return true + } + + // This is a struct method + for _, field := range funcDecl.Recv.List { + switch f := field.Type.(type) { + case *ast.StarExpr: + // This is a struct pointer method + ident, ok := f.X.(*ast.Ident) // _ ? + if !ok { + continue + } + + // Check this method is for this struct + if ident.Name != boundStruct.Name { + continue + } + + // If this method is not Public, ignore + if string(funcDecl.Name.Name[0]) != strings.ToUpper((string(funcDecl.Name.Name[0]))) { + continue + } + + // Create our struct + structMethod := &Method{ + Name: funcDecl.Name.Name, + Comments: parseComments(funcDecl.Doc), + } + + // Save the input parameters + if funcDecl.Type.Params != nil { + for _, inputField := range funcDecl.Type.Params.List { + fields, err := p.parseField(fileAst, inputField, boundStruct.Package) + if err != nil { + parseError = err + return false + } + + // If this field was a struct, flag that it is used as data + if len(fields) > 0 { + if fields[0].Struct != nil { + fields[0].Struct.IsUsedAsData = true + } + } + + structMethod.Inputs = append(structMethod.Inputs, fields...) + } + } + + // Save the output parameters + if funcDecl.Type.Results != nil { + for _, outputField := range funcDecl.Type.Results.List { + fields, err := p.parseField(fileAst, outputField, boundStruct.Package) + if err != nil { + parseError = err + return false + } + + // If this field was a struct, flag that it is used as data + if len(fields) > 0 { + if fields[0].Struct != nil { + fields[0].Struct.IsUsedAsData = true + } + } + + structMethod.Returns = append(structMethod.Returns, fields...) + } + } + + // Append this method to the parsed struct + boundStruct.Methods = append(boundStruct.Methods, structMethod) + + default: + // Unsupported + continue + } + } + } + return true + }) + + // If we got an error, return it + if parseError != nil { + return parseError + } + } + + return nil +} + +// InputsAsTSText generates a string with the method inputs +// formatted in a way acceptable to Typescript +func (m *Method) InputsAsTSText(pkgName string) string { + var inputs []string + + for _, input := range m.Inputs { + inputText := fmt.Sprintf("%s: %s", input.Name, goTypeToTS(input, pkgName)) + inputs = append(inputs, inputText) + } + + return strings.Join(inputs, ", ") +} + +// OutputsAsTSText generates a string with the method inputs +// formatted in a way acceptable to Javascript +func (m *Method) OutputsAsTSText(pkgName string) string { + + if len(m.Returns) == 0 { + return "void" + } + + var result []string + + for _, output := range m.Returns { + result = append(result, goTypeToTS(output, pkgName)) + } + return strings.Join(result, ", ") +} + +// OutputsAsTSDeclarationText generates a string with the method inputs +// formatted in a way acceptable to Javascript +func (m *Method) OutputsAsTSDeclarationText(pkgName string) string { + + if len(m.Returns) == 0 { + return "void" + } + + var result []string + + for _, output := range m.Returns { + result = append(result, goTypeToTSDeclaration(output, pkgName)) + } + return strings.Join(result, ", ") +} + +// InputsAsJSText generates a string with the method inputs +// formatted in a way acceptable to Javascript +func (m *Method) InputsAsJSText() string { + var inputs []string + + for _, input := range m.Inputs { + inputs = append(inputs, input.Name) + } + + return strings.Join(inputs, ", ") +} diff --git a/v2/pkg/parser/package.d copy.template b/v2/pkg/parser/package.d copy.template new file mode 100644 index 000000000..5206b9621 --- /dev/null +++ b/v2/pkg/parser/package.d copy.template @@ -0,0 +1,31 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +{{- if .DeclarationReferences }} +{{range .DeclarationReferences}} +/// {{end}}{{- end}} + +export namespace {{.Name}} { {{range .Structs}} + {{- if or .IsBound .IsUsedAsData}} + {{if .Comments }}{{range .Comments}}// {{ . }}{{end}}{{- end}} + interface {{.Name}} { {{ if .IsUsedAsData }} + {{- range .Fields}}{{if .Comments }} + {{range .Comments}}//{{ . }}{{end}}{{- end}} + {{.Name}}: {{.TypeAsTSType $.Name}}; {{- end}} {{ end }} + {{- if .IsBound }} + {{- range .Methods}} + /**{{if .Comments }} +{{range .Comments}} * {{ . }}{{end}} + *{{end}} + * @function {{.Name}} +{{range .Inputs}} * @param {{"{"}}{{.JSType}}{{"}"}} {{.Name}} +{{end}} * + * @returns {Promise<{{.OutputsAsTSText $.Name}}>} + */ + {{.Name}}({{.InputsAsTSText $.Name}}): Promise<{{.OutputsAsTSText $.Name}}>; + {{- end}}{{end}} + }{{- end}} + {{end}} + +} + diff --git a/v2/pkg/parser/package.d.template b/v2/pkg/parser/package.d.template new file mode 100644 index 000000000..6eccdb0fe --- /dev/null +++ b/v2/pkg/parser/package.d.template @@ -0,0 +1,42 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +{{- range .Structs}} +{{- if .IsBound}} +export namespace {{.Name}} { + {{- range .Methods}} + {{- if .Comments }} + {{range .Comments}} + // {{ . }}{{end}} + {{- end}} + function {{.Name}}({{.InputsAsTSText $.Name}}): Promise<{{.OutputsAsTSDeclarationText $.Name}}>; + {{- end}} +} +{{- end}} +{{- if .IsUsedAsData}} +{{if .Comments }} +/** +{{range .Comments}} *{{ . }}{{end}} + */ +export type {{.Name}} = { +{{- range .Fields}} + {{- if not .Ignored}} + {{- if .Comments }}{{range .Comments}} + //{{ . }}{{end}}{{- end}} + {{ .AsTSDeclaration $.Name}}; {{- end}} +{{- end}} +}; + +/** +{{if .Comments }}{{range .Comments}} *{{ . }}{{end}}{{end}} + * @typedef {object} {{.Name}} +{{- range .Fields}}{{- if not .JSONOptions.Ignored }} + * @property {{"{"}}{{.TypeForPropertyDoc}}{{"}"}} {{.NameForPropertyDoc}} {{- if .Comments}} - {{- range .Comments}}{{ . }}{{- end}}{{- end}}{{- end}} +{{- end}} + */ +export var {{.Name}}: any; + +{{- end}} +{{- end}} +{{- end}} diff --git a/v2/pkg/parser/package.go b/v2/pkg/parser/package.go new file mode 100644 index 000000000..e9357efd0 --- /dev/null +++ b/v2/pkg/parser/package.go @@ -0,0 +1,152 @@ +package parser + +import ( + "go/ast" + "strings" + + "github.com/leaanthony/slicer" + "golang.org/x/tools/go/packages" +) + +// Package is a wrapper around the go parsed package +type Package struct { + + // A unique Name for this package. + // This is calculated and may not be the same as the one + // defined in Go - but that's ok! + Name string + + // the package we are wrapping + Gopackage *packages.Package + + // a list of struct names that are bound in this package + boundStructs slicer.StringSlicer + + // Structs used in this package + parsedStructs map[string]*Struct + + // A list of external packages we reference from this package + externalReferences slicer.InterfaceSlicer +} + +func newPackage(pkg *packages.Package) *Package { + return &Package{ + Gopackage: pkg, + parsedStructs: make(map[string]*Struct), + } +} + +func (p *Package) getWailsImportName(file *ast.File) string { + // Scan the imports for the wails v2 import + for _, details := range file.Imports { + if details.Path.Value == `"github.com/wailsapp/wails/v2"` { + if details.Name != nil { + return details.Name.Name + } + + // Get the import name from the package + imp := p.getImportByPath("github.com/wailsapp/wails/v2") + if imp != nil { + return imp.Name + } + } + } + return "" +} + +func (p *Package) getImportByName(importName string, file *ast.File) *packages.Package { + + // Check if the file has aliased the import + for _, imp := range file.Imports { + if imp.Name != nil { + if imp.Name.Name == importName { + // Yes it has. Get the import by path + return p.getImportByPath(imp.Path.Value) + } + } + } + + // We need to find which package import has this name + for _, imp := range p.Gopackage.Imports { + if imp.Name == importName { + return imp + } + } + + // Looks like this package is outside the project... + return nil +} + +func (p *Package) getImportByPath(packagePath string) *packages.Package { + packagePath = strings.Trim(packagePath, "\"") + return p.Gopackage.Imports[packagePath] +} + +func (p *Package) getStruct(structName string) *Struct { + return p.parsedStructs[structName] +} + +func (p *Package) addStruct(strct *Struct) { + p.parsedStructs[strct.Name] = strct +} + +// HasBoundStructs returns true if any of its structs +// are bound +func (p *Package) HasBoundStructs() bool { + + for _, strct := range p.parsedStructs { + if strct.IsBound { + return true + } + } + + return false +} + +// HasDataStructs returns true if any of its structs +// are used as data +func (p *Package) HasDataStructs() bool { + for _, strct := range p.parsedStructs { + if strct.IsUsedAsData { + return true + } + } + + return false +} + +// ShouldGenerate returns true when this package should be generated +func (p *Package) ShouldGenerate() bool { + return p.HasBoundStructs() || p.HasDataStructs() +} + +// DeclarationReferences returns a list of external packages +// we reference from this package +func (p *Package) DeclarationReferences() []string { + + var referenceNames slicer.StringSlicer + + // Generics can't come soon enough! + p.externalReferences.Each(func(p interface{}) { + referenceNames.Add(p.(*Package).Name) + }) + + return referenceNames.AsSlice() +} + +// addExternalReference saves the given package as an external reference +func (p *Package) addExternalReference(pkg *Package) { + p.externalReferences.AddUnique(pkg) +} + +// Structs returns the structs that we want to generate +func (p *Package) Structs() []*Struct { + + var result []*Struct + + for _, elem := range p.parsedStructs { + result = append(result, elem) + } + + return result +} diff --git a/v2/pkg/parser/package.json b/v2/pkg/parser/package.json new file mode 100644 index 000000000..5d0ddb820 --- /dev/null +++ b/v2/pkg/parser/package.json @@ -0,0 +1,13 @@ +{ + "name": "go", + "version": "1.0.0", + "description": "Auto generated module wrapping your Wails backend", + "main": "index.js", + "types": "index.d.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/v2/pkg/parser/package.template b/v2/pkg/parser/package.template new file mode 100644 index 000000000..2c7362df1 --- /dev/null +++ b/v2/pkg/parser/package.template @@ -0,0 +1,44 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +{{- if .DeclarationReferences }} +{{range .DeclarationReferences}} +const {{.}} = require('./_{{.}}');{{end}}{{- end}} + +{{- range $struct := .Structs }} +{{- if .IsUsedAsData }} + +/** +{{if .Comments }}{{range .Comments}} *{{ . }}{{end}}{{end}} + * @typedef {object} {{.Name}} +{{- range .Fields}}{{- if not .JSONOptions.Ignored }} + * @property {{"{"}}{{.TypeForPropertyDoc}}{{"}"}} {{.NameForPropertyDoc}} {{- if .Comments}} - {{- range .Comments}}{{ . }}{{- end}}{{- end}}{{- end}} +{{- end}} + */ +export var {{.Name}}; + +{{- end}} +{{- if .IsBound }} +{{- if .Methods }} + +{{if .Comments }}{{range .Comments}}// {{ . }}{{end}}{{end}} +export const {{.Name}} = { +{{range .Methods }} + /**{{if .Comments }} +{{range .Comments}} * {{ . }}{{end}} + *{{end}} + * @function {{.Name}} +{{range .Inputs}} * @param {{"{"}}{{.JSType}}{{"}"}} {{.Name}} +{{end}} * + * @returns {Promise<{{.OutputsAsTSText $.Name}}>} + */ + {{.Name}}: function({{.InputsAsJSText}}) { + return window.backend.{{$.Name}}.{{$struct.Name}}.{{.Name}}({{.InputsAsJSText}}); + }, +{{end}} +} + +{{- end}} +{{- end}} +{{- end}} diff --git a/v2/pkg/parser/parseBoundStructs.go b/v2/pkg/parser/parseBoundStructs.go new file mode 100644 index 000000000..c26839433 --- /dev/null +++ b/v2/pkg/parser/parseBoundStructs.go @@ -0,0 +1,75 @@ +package parser + +import "go/ast" + +func (p *Parser) parseBoundStructs(pkg *Package) error { + + // Loop over the bound structs + for _, structName := range pkg.boundStructs.AsSlice() { + strct, err := p.parseStruct(pkg, structName) + if err != nil { + return err + } + strct.IsBound = true + } + + return nil +} + +// ParseStruct will attempt to parse the given struct using +// the package it references +func (p *Parser) parseStruct(pkg *Package, structName string) (*Struct, error) { + + // Check the parser cache for this struct + result := pkg.getStruct(structName) + if result != nil { + return result, nil + } + + // Iterate through the whole package looking for the bound structs + for _, fileAst := range pkg.Gopackage.Syntax { + + // Track errors + var parseError error + + ast.Inspect(fileAst, func(n ast.Node) bool { + if genDecl, ok := n.(*ast.GenDecl); ok { + for _, spec := range genDecl.Specs { + if typeSpec, ok := spec.(*ast.TypeSpec); ok { + if structType, ok := typeSpec.Type.(*ast.StructType); ok { + structDefinitionName := typeSpec.Name.Name + if structDefinitionName == structName { + + // Create the new struct + result = &Struct{Name: structName, Package: pkg} + + // Save comments + result.Comments = parseComments(genDecl.Doc) + + parseError = p.parseStructMethods(result) + if parseError != nil { + return false + } + + // Parse the struct fields + parseError = p.parseStructFields(fileAst, structType, result) + + // Save this struct + pkg.addStruct(result) + + return false + } + } + } + } + } + return true + }) + + // If we got an error, return it + if parseError != nil { + return nil, parseError + } + } + return result, nil +} diff --git a/v2/pkg/parser/parseStructFields.go b/v2/pkg/parser/parseStructFields.go new file mode 100644 index 000000000..cdca538d1 --- /dev/null +++ b/v2/pkg/parser/parseStructFields.go @@ -0,0 +1,35 @@ +package parser + +import ( + "go/ast" + + "github.com/pkg/errors" +) + +func (p *Parser) parseStructFields(fileAst *ast.File, structType *ast.StructType, boundStruct *Struct) error { + + // Parse the fields + for _, field := range structType.Fields.List { + fields, err := p.parseField(fileAst, field, boundStruct.Package) + if err != nil { + return errors.Wrap(err, "error parsing struct "+boundStruct.Name) + } + + // If this field was a struct, flag that it is used as data + if len(fields) > 0 { + if fields[0].Struct != nil { + fields[0].Struct.IsUsedAsData = true + } + } + + // If this field name is lowercase, it won't be exported + for _, field := range fields { + if !startsWithLowerCaseLetter(field.Name) { + boundStruct.Fields = append(boundStruct.Fields, field) + } + } + + } + + return nil +} diff --git a/v2/pkg/parser/parser.go b/v2/pkg/parser/parser.go new file mode 100644 index 000000000..5b49b42da --- /dev/null +++ b/v2/pkg/parser/parser.go @@ -0,0 +1,122 @@ +// Package parser provides the ability to parse the data that is bound in Wails projects. +// Using this, it can also generate a Javascript module that represents the DTOs used, as +// well as providing wrappers for bound methods. +package parser + +import ( + "go/token" + + "github.com/pkg/errors" + "golang.org/x/tools/go/packages" +) + +// Parser is the Wails project parser +type Parser struct { + + // Placeholders for Go's parser + fileSet *token.FileSet + + // The packages we parse + // The map key is the package ID + packages map[string]*Package +} + +// NewParser creates a new Wails project parser +func NewParser() *Parser { + return &Parser{ + fileSet: token.NewFileSet(), + packages: make(map[string]*Package), + } +} + +// ParseProject will parse the Wails project in the given directory +func (p *Parser) ParseProject(dir string) error { + + var err error + + err = p.loadPackages(dir) + if err != nil { + return err + } + + // Find all the bound structs + for _, pkg := range p.packages { + err = p.findBoundStructs(pkg) + if err != nil { + return err + } + } + + // Parse the structs + for _, pkg := range p.packages { + err = p.parseBoundStructs(pkg) + if err != nil { + return err + } + } + + // Resolve package names + // We do this because some packages may have the same name + p.resolvePackageNames() + + return nil +} + +func (p *Parser) loadPackages(projectPath string) error { + mode := packages.NeedName | + packages.NeedFiles | + packages.NeedSyntax | + packages.NeedTypes | + packages.NeedImports | + packages.NeedTypesInfo | + packages.NeedModule + + cfg := &packages.Config{Fset: p.fileSet, Mode: mode, Dir: projectPath} + pkgs, err := packages.Load(cfg, "./...") + if err != nil { + return errors.Wrap(err, "Problem loading packages") + } + // Check for errors + var parseError error + for _, pkg := range pkgs { + for _, err := range pkg.Errors { + if parseError == nil { + parseError = errors.New(err.Error()) + } else { + parseError = errors.Wrap(parseError, err.Error()) + } + } + } + + if parseError != nil { + return parseError + } + + // Create a map of packages + for _, pkg := range pkgs { + p.packages[pkg.ID] = newPackage(pkg) + } + + return nil +} + +func (p *Parser) getPackageByID(id string) *Package { + return p.packages[id] +} + +func (p *Parser) packagesToGenerate() []*Package { + + var result []*Package + + for _, pkg := range p.packages { + if pkg.ShouldGenerate() { + result = append(result, pkg) + } + } + + return result +} + +type ParserReport struct { + Packages []*Package +} diff --git a/v2/pkg/parser/resolvePackageReferences.go b/v2/pkg/parser/resolvePackageReferences.go new file mode 100644 index 000000000..081b3f8cb --- /dev/null +++ b/v2/pkg/parser/resolvePackageReferences.go @@ -0,0 +1,35 @@ +package parser + +import ( + "fmt" + + "github.com/leaanthony/slicer" +) + +// resolvePackageNames will deterine the names for the packages, allowing +// us to create a flat structure for the imports in the frontend module +func (p *Parser) resolvePackageNames() { + + // A cache for the names + var packageNameCache slicer.StringSlicer + + // Process each package + for _, pkg := range p.packages { + pkgName := pkg.Gopackage.Name + + // Check for collision + if packageNameCache.Contains(pkgName) { + // https://www.youtube.com/watch?v=otNNGROI0Cs !!!!! + + // We start at 2 because having both "pkg" and "pkg1" is 🙄 + count := 2 + for ok := true; ok; ok = packageNameCache.Contains(pkgName) { + pkgName = fmt.Sprintf("%s%d", pkg.Gopackage.Name, count) + } + } + + // Save the name! + packageNameCache.Add(pkgName) + pkg.Name = pkgName + } +} diff --git a/v2/pkg/parser/struct.go b/v2/pkg/parser/struct.go new file mode 100644 index 000000000..6108309bd --- /dev/null +++ b/v2/pkg/parser/struct.go @@ -0,0 +1,68 @@ +package parser + +import ( + "fmt" + "go/ast" + + "github.com/pkg/errors" +) + +// Struct represents a struct that is used by the frontend +// in a Wails project +type Struct struct { + + // The name of the struct + Name string + + // The package this was declared in + Package *Package + + // Comments for the struct + Comments []string + + // The fields used in this struct + Fields []*Field + + // The methods available to the front end + Methods []*Method + + // Indicates if this struct is bound to the app + IsBound bool + + // Indicates if this struct is used as data + IsUsedAsData bool +} + +func parseStructNameFromStarExpr(starExpr *ast.StarExpr) (string, string, error) { + pkg := "" + name := "" + // Determine the FQN + switch x := starExpr.X.(type) { + case *ast.SelectorExpr: + switch i := x.X.(type) { + case *ast.Ident: + pkg = i.Name + default: + // TODO: Store warnings? + return "", "", errors.WithStack(fmt.Errorf("unknown type in selector for *ast.SelectorExpr: %+v", i)) + } + + name = x.Sel.Name + + // TODO: IS this used? + case *ast.StarExpr: + switch s := x.X.(type) { + case *ast.Ident: + name = s.Name + default: + // TODO: Store warnings? + return "", "", errors.WithStack(fmt.Errorf("unknown type in selector for *ast.StarExpr: %+v", s)) + } + case *ast.Ident: + name = x.Name + default: + // TODO: Store warnings? + return "", "", errors.WithStack(fmt.Errorf("unknown type in selector for *ast.StarExpr: %+v", starExpr)) + } + return pkg, name, nil +} diff --git a/v2/pkg/parser/testproject/basic.go b/v2/pkg/parser/testproject/basic.go new file mode 100644 index 000000000..cce57e18f --- /dev/null +++ b/v2/pkg/parser/testproject/basic.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + + "testproject/mypackage" + + "github.com/wailsapp/wails/v2" +) + +// Basic application struct +type Basic struct { + runtime *wails.Runtime +} + +// // Another application struct +// type Another struct { +// runtime *wails.Runtime +// } + +// func (a *Another) Doit() { + +// } + +// // newBasicPointer creates a new Basic application struct +// func newBasicPointer() *Basic { +// return &Basic{} +// } + +// // newBasic creates a new Basic application struct +// func newBasic() Basic { +// return Basic{} +// } + +// WailsInit is called at application startup +func (b *Basic) WailsInit(runtime *wails.Runtime) error { + // Perform your setup here + b.runtime = runtime + runtime.Window.SetTitle("jsbundle") + return nil +} + +// WailsShutdown is called at application termination +func (b *Basic) WailsShutdown() { + // Perform your teardown here +} + +// NewPerson creates a new person +func (b *Basic) NewPerson(name string, age int) *mypackage.Person { + return &mypackage.Person{Name: name, Age: age} +} + +// Greet returns a greeting for the given name +func (b *Basic) Greet(name string) string { + return fmt.Sprintf("Hello %s!", name) +} + +// MultipleGreets returns greetings for the given name +func (b *Basic) MultipleGreets(_ string) []string { + return []string{"hi", "hello", "croeso!"} +} + +// RemovePerson Removes the given person +func (b *Basic) RemovePerson(_ *mypackage.Person) { + // dummy +} diff --git a/v2/pkg/parser/testproject/go.mod b/v2/pkg/parser/testproject/go.mod new file mode 100644 index 000000000..21f9e0d7e --- /dev/null +++ b/v2/pkg/parser/testproject/go.mod @@ -0,0 +1,9 @@ +module testproject + +go 1.13 + +require ( + github.com/wailsapp/wails/v2 v2.0.0-alpha +) + +replace github.com/wailsapp/wails/v2 v2.0.0-alpha => /home/lea/Data/projects/wails/v2 diff --git a/v2/pkg/parser/testproject/go.sum b/v2/pkg/parser/testproject/go.sum new file mode 100644 index 000000000..dae10ccef --- /dev/null +++ b/v2/pkg/parser/testproject/go.sum @@ -0,0 +1,83 @@ +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0= +github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU= +github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs= +github.com/tdewolff/minify/v2 v2.9.5/go.mod h1:jshtBj/uUJH6JX1fuxTLnnHOA1RVJhF5MM+leJzDKb4= +github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ= +github.com/tdewolff/parse/v2 v2.5.3/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= diff --git a/v2/pkg/parser/testproject/main.go b/v2/pkg/parser/testproject/main.go new file mode 100644 index 000000000..0968d9112 --- /dev/null +++ b/v2/pkg/parser/testproject/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "testproject/mypackage" + + "github.com/wailsapp/wails/v2" +) + +func main() { + // Create application with options + app := wails.CreateApp("jsbundle", 1024, 768) + + /***** Struct Literal *****/ + + // Local struct pointer literal *WORKING* + app.Bind(&Basic{}) + + // External struct pointer literal + app.Bind(&mypackage.Manager{}) + +} diff --git a/v2/pkg/parser/testproject/mypackage/mypackage.go b/v2/pkg/parser/testproject/mypackage/mypackage.go new file mode 100644 index 000000000..5dcac83e3 --- /dev/null +++ b/v2/pkg/parser/testproject/mypackage/mypackage.go @@ -0,0 +1,36 @@ +// Package mypackage does all the things a mypackage can do +package mypackage + +type Address struct { + Number int + Street string + Town string + Postcode string +} + +// Person defines a Person in the application +type Person struct { + // Name is a name + Name string + Age int + Address *Address +} + +// Manager is the Mr Manager +type Manager struct { + Name string + TwoIC *Person +} + +// Hire me some peoples! +func (m *Manager) Hire(name, test string, bob int) *Person { + return &Person{Name: name} +} + +// func NewManagerPointer() *Manager { +// return &Manager{} +// } + +// func NewManager() Manager { +// return Manager{} +// } diff --git a/v2/pkg/runtime/browser.go b/v2/pkg/runtime/browser.go new file mode 100644 index 000000000..28ffc5e05 --- /dev/null +++ b/v2/pkg/runtime/browser.go @@ -0,0 +1,11 @@ +package runtime + +import ( + "context" +) + +// BrowserOpenURL uses the system default browser to open the url +func BrowserOpenURL(ctx context.Context, url string) { + appFrontend := getFrontend(ctx) + appFrontend.BrowserOpenURL(url) +} diff --git a/v2/pkg/runtime/dialog.go b/v2/pkg/runtime/dialog.go new file mode 100644 index 000000000..c8b6333a8 --- /dev/null +++ b/v2/pkg/runtime/dialog.go @@ -0,0 +1,57 @@ +package runtime + +import ( + "context" + "github.com/wailsapp/wails/v2/internal/frontend" +) + +// FileFilter defines a filter for dialog boxes +type FileFilter = frontend.FileFilter + +// OpenDialogOptions contains the options for the OpenDialogOptions runtime method +type OpenDialogOptions = frontend.OpenDialogOptions + +// SaveDialogOptions contains the options for the SaveDialog runtime method +type SaveDialogOptions = frontend.SaveDialogOptions + +type DialogType = frontend.DialogType + +const ( + InfoDialog = frontend.InfoDialog + WarningDialog = frontend.WarningDialog + ErrorDialog = frontend.ErrorDialog + QuestionDialog = frontend.QuestionDialog +) + +// MessageDialogOptions contains the options for the Message dialogs, EG Info, Warning, etc runtime methods +type MessageDialogOptions = frontend.MessageDialogOptions + +// OpenDirectoryDialog prompts the user to select a directory +func OpenDirectoryDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error) { + appFrontend := getFrontend(ctx) + return appFrontend.OpenDirectoryDialog(dialogOptions) +} + +// OpenFileDialog prompts the user to select a file +func OpenFileDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error) { + appFrontend := getFrontend(ctx) + return appFrontend.OpenFileDialog(dialogOptions) +} + +// OpenMultipleFilesDialog prompts the user to select a file +func OpenMultipleFilesDialog(ctx context.Context, dialogOptions OpenDialogOptions) ([]string, error) { + appFrontend := getFrontend(ctx) + return appFrontend.OpenMultipleFilesDialog(dialogOptions) +} + +// SaveFileDialog prompts the user to select a file +func SaveFileDialog(ctx context.Context, dialogOptions SaveDialogOptions) (string, error) { + appFrontend := getFrontend(ctx) + return appFrontend.SaveFileDialog(dialogOptions) +} + +// MessageDialog show a message dialog to the user +func MessageDialog(ctx context.Context, dialogOptions MessageDialogOptions) (string, error) { + appFrontend := getFrontend(ctx) + return appFrontend.MessageDialog(dialogOptions) +} diff --git a/v2/pkg/runtime/events.go b/v2/pkg/runtime/events.go new file mode 100644 index 000000000..440c7bdf6 --- /dev/null +++ b/v2/pkg/runtime/events.go @@ -0,0 +1,36 @@ +package runtime + +import ( + "context" +) + +// EventsOn registers a listener for the given event name +func EventsOn(ctx context.Context, eventName string, callback func(optionalData ...interface{})) { + events := getEvents(ctx) + events.On(eventName, callback) +} + +// EventsOff unregisters a listener for the given event name +func EventsOff(ctx context.Context, eventName string) { + events := getEvents(ctx) + events.Off(eventName) +} + +// EventsOnce registers a listener for the given event name. After the first callback, the +// listener is deleted. +func EventsOnce(ctx context.Context, eventName string, callback func(optionalData ...interface{})) { + events := getEvents(ctx) + events.Once(eventName, callback) +} + +// EventsOnMultiple registers a listener for the given event name, that may be called a maximum of 'counter' times +func EventsOnMultiple(ctx context.Context, eventName string, callback func(optionalData ...interface{}), counter int) { + events := getEvents(ctx) + events.OnMultiple(eventName, callback, counter) +} + +// EventsEmit pass through +func EventsEmit(ctx context.Context, eventName string, optionalData ...interface{}) { + events := getEvents(ctx) + events.Emit(eventName, optionalData...) +} diff --git a/v2/pkg/runtime/log.go b/v2/pkg/runtime/log.go new file mode 100644 index 000000000..5f46c61df --- /dev/null +++ b/v2/pkg/runtime/log.go @@ -0,0 +1,54 @@ +package runtime + +import ( + "context" + "github.com/wailsapp/wails/v2/pkg/logger" +) + +// LogPrint prints a Print level message +func LogPrint(ctx context.Context, message string) { + myLogger := getLogger(ctx) + myLogger.Print(message) +} + +// LogTrace prints a Trace level message +func LogTrace(ctx context.Context, message string) { + myLogger := getLogger(ctx) + myLogger.Trace(message) +} + +// LogDebug prints a Debug level message +func LogDebug(ctx context.Context, message string) { + myLogger := getLogger(ctx) + myLogger.Debug(message) +} + +// LogInfo prints a Info level message +func LogInfo(ctx context.Context, message string) { + myLogger := getLogger(ctx) + myLogger.Info(message) +} + +// LogWarning prints a Warning level message +func LogWarning(ctx context.Context, message string) { + myLogger := getLogger(ctx) + myLogger.Warning(message) +} + +// LogError prints a Error level message +func LogError(ctx context.Context, message string) { + myLogger := getLogger(ctx) + myLogger.Error(message) +} + +// LogFatal prints a Fatal level message +func LogFatal(ctx context.Context, message string) { + myLogger := getLogger(ctx) + myLogger.Fatal(message) +} + +// LogSetLogLevel sets the log level +func LogSetLogLevel(ctx context.Context, level logger.LogLevel) { + myLogger := getLogger(ctx) + myLogger.SetLogLevel(level) +} diff --git a/v2/pkg/runtime/menu.go b/v2/pkg/runtime/menu.go new file mode 100644 index 000000000..176c9bb1d --- /dev/null +++ b/v2/pkg/runtime/menu.go @@ -0,0 +1,16 @@ +package runtime + +import ( + "context" + "github.com/wailsapp/wails/v2/pkg/menu" +) + +func MenuSetApplicationMenu(ctx context.Context, menu *menu.Menu) { + frontend := getFrontend(ctx) + frontend.MenuSetApplicationMenu(menu) +} + +func MenuUpdateApplicationMenu(ctx context.Context) { + frontend := getFrontend(ctx) + frontend.MenuUpdateApplicationMenu() +} diff --git a/v2/pkg/runtime/runtime.go b/v2/pkg/runtime/runtime.go new file mode 100644 index 000000000..9dbdffb8e --- /dev/null +++ b/v2/pkg/runtime/runtime.go @@ -0,0 +1,65 @@ +package runtime + +import ( + "context" + "github.com/wailsapp/wails/v2/internal/frontend" + "github.com/wailsapp/wails/v2/internal/logger" + "log" + goruntime "runtime" +) + +func getFrontend(ctx context.Context) frontend.Frontend { + if ctx == nil { + pc, _, _, _ := goruntime.Caller(1) + funcName := goruntime.FuncForPC(pc).Name() + log.Fatalf("cannot call '%s': context is nil", funcName) + } + result := ctx.Value("frontend") + if result != nil { + return result.(frontend.Frontend) + } + pc, _, _, _ := goruntime.Caller(1) + funcName := goruntime.FuncForPC(pc).Name() + log.Fatalf("cannot call '%s': Application not initialised", funcName) + return nil +} +func getLogger(ctx context.Context) *logger.Logger { + if ctx == nil { + pc, _, _, _ := goruntime.Caller(1) + funcName := goruntime.FuncForPC(pc).Name() + log.Fatalf("cannot call '%s': context is nil", funcName) + } + result := ctx.Value("logger") + if result != nil { + return result.(*logger.Logger) + } + pc, _, _, _ := goruntime.Caller(1) + funcName := goruntime.FuncForPC(pc).Name() + log.Fatalf("cannot call '%s': Application not initialised", funcName) + return nil +} + +func getEvents(ctx context.Context) frontend.Events { + if ctx == nil { + pc, _, _, _ := goruntime.Caller(1) + funcName := goruntime.FuncForPC(pc).Name() + log.Fatalf("cannot call '%s': context is nil", funcName) + } + result := ctx.Value("events") + if result != nil { + return result.(frontend.Events) + } + pc, _, _, _ := goruntime.Caller(1) + funcName := goruntime.FuncForPC(pc).Name() + log.Fatalf("cannot call '%s': Application not initialised", funcName) + return nil +} + +// Quit the application +func Quit(ctx context.Context) { + if ctx == nil { + log.Fatalf("cannot call Quit: context is nil") + } + appFrontend := getFrontend(ctx) + appFrontend.Quit() +} diff --git a/v2/pkg/runtime/window.go b/v2/pkg/runtime/window.go new file mode 100644 index 000000000..e492134f1 --- /dev/null +++ b/v2/pkg/runtime/window.go @@ -0,0 +1,112 @@ +package runtime + +import ( + "context" + + "github.com/wailsapp/wails/v2/pkg/options" +) + +// WindowSetTitle sets the title of the window +func WindowSetTitle(ctx context.Context, title string) { + appFrontend := getFrontend(ctx) + appFrontend.WindowSetTitle(title) +} + +// WindowFullscreen makes the window fullscreen +func WindowFullscreen(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowFullscreen() +} + +// WindowUnFullscreen makes the window UnFullscreen +func WindowUnFullscreen(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowUnFullscreen() +} + +// WindowCenter the window on the current screen +func WindowCenter(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowCenter() +} + +// WindowReload will reload the window contents +func WindowReload(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowReload() +} + +// WindowShow shows the window if hidden +func WindowShow(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowShow() +} + +// WindowHide the window +func WindowHide(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowHide() +} + +// WindowSetSize sets the size of the window +func WindowSetSize(ctx context.Context, width int, height int) { + appFrontend := getFrontend(ctx) + appFrontend.WindowSetSize(width, height) +} + +func WindowGetSize(ctx context.Context) (int, int) { + appFrontend := getFrontend(ctx) + return appFrontend.WindowGetSize() +} + +// WindowSetMinSize sets the minimum size of the window +func WindowSetMinSize(ctx context.Context, width int, height int) { + appFrontend := getFrontend(ctx) + appFrontend.WindowSetMinSize(width, height) +} + +// WindowSetMaxSize sets the maximum size of the window +func WindowSetMaxSize(ctx context.Context, width int, height int) { + appFrontend := getFrontend(ctx) + appFrontend.WindowSetMaxSize(width, height) +} + +// WindowSetPosition sets the position of the window +func WindowSetPosition(ctx context.Context, x int, y int) { + appFrontend := getFrontend(ctx) + appFrontend.WindowSetPos(x, y) +} + +func WindowGetPos(ctx context.Context) (int, int) { + appFrontend := getFrontend(ctx) + return appFrontend.WindowGetPos() +} + +// WindowMaximise the window +func WindowMaximise(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowMaximise() +} + +// WindowUnmaximise the window +func WindowUnmaximise(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowUnmaximise() +} + +// WindowMinimise the window +func WindowMinimise(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowMinimise() +} + +// WindowUnminimise the window +func WindowUnminimise(ctx context.Context) { + appFrontend := getFrontend(ctx) + appFrontend.WindowUnminimise() +} + +func WindowSetRGBA(ctx context.Context, col *options.RGBA) { + appFrontend := getFrontend(ctx) + appFrontend.WindowSetRGBA(col) +} diff --git a/v2/pkg/str/str.go b/v2/pkg/str/str.go new file mode 100644 index 000000000..d27d5dad7 --- /dev/null +++ b/v2/pkg/str/str.go @@ -0,0 +1,10 @@ +package str + +import ( + "fmt" + "time" +) + +func UnixNow() string { + return fmt.Sprintf("%+v", time.Now().Unix()) +} diff --git a/v2/wails.go b/v2/wails.go new file mode 100644 index 000000000..33d07a561 --- /dev/null +++ b/v2/wails.go @@ -0,0 +1,25 @@ +// Package wails is the main package of the Wails project. +// It is used by client applications. +package wails + +import ( + app "github.com/wailsapp/wails/v2/internal/appng" + "github.com/wailsapp/wails/v2/pkg/options" +) + +// Run creates an application based on the given config and executes it +func Run(options *options.App) error { + + // Call an Init method manually + err := Init() + if err != nil { + return err + } + + mainapp, err := app.CreateApp(options) + if err != nil { + return err + } + + return mainapp.Run() +} diff --git a/website/.gitignore b/website/.gitignore new file mode 100644 index 000000000..b2d6de306 --- /dev/null +++ b/website/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/website/README.md b/website/README.md new file mode 100644 index 000000000..26cec95bc --- /dev/null +++ b/website/README.md @@ -0,0 +1,36 @@ +# Website + +This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. + +### Installation + +``` +$ yarn +``` + +### Local Development + +``` +$ yarn start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without +having to restart the server. + +### Build + +``` +$ yarn build +``` + +This command generates static content into the `build` directory and can be served using any static contents hosting +service. + +### Deployment + +``` +$ GIT_USER= USE_SSH=true yarn deploy +``` + +If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to +the `gh-pages` branch. diff --git a/website/babel.config.js b/website/babel.config.js new file mode 100644 index 000000000..0adade1fb --- /dev/null +++ b/website/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/website/blog/2021-09-27-v2-beta1-release-notes.mdx b/website/blog/2021-09-27-v2-beta1-release-notes.mdx new file mode 100644 index 000000000..95d5bd951 --- /dev/null +++ b/website/blog/2021-09-27-v2-beta1-release-notes.mdx @@ -0,0 +1,182 @@ +--- +slug: wails-v2-beta-for-windows +title: Wails v2 Beta for Windows +authors: [leaanthony] +tags: [wails, v2] +--- + + +
+ +
+
+ + +When I first announced Wails on Reddit, just over 2 years ago from a train in Sydney, I did not expect it to get much +attention. A few days later, a prolific tech vlogger released a tutorial video, gave it a positive review and from that +point on, interest in the project has skyrocketed. + +It was clear that people were excited about adding web frontends to their Go projects, and almost immediately pushed the +project beyond the proof of concept that I had created. +At the time, Wails used the [webview](https://github.com/webview/webview) project to handle the frontend, and the only +option for Windows was the IE11 renderer. Many bug reports were rooted in this limitation: poor JavaScript/CSS support +and no dev tools to debug it. This was a frustrating development experience but there wasn't much that could have been +done to rectify it. + +For a long time, I'd firmly believed that Microsoft would eventually have to sort out their browser situation. +The world was moving on, frontend development was booming and IE wasn't cutting it. +When Microsoft announced the move to using Chromium as the basis for their new browser direction, I knew it was only a +matter of time until Wails could use it, and move the Windows developer experience to the next level. + +Today, I am pleased to announce: **Wails v2 Beta for Windows**! There's a huge amount to unpack in this release, so +grab a drink, take a seat and we'll begin... + +### No CGO Dependency! + +No, I'm not joking: *No* *CGO* *dependency* 🤯! The thing about Windows is that, unlike MacOS and Linux, it doesn't +come with a default compiler. In addition, CGO requires a mingw compiler and there's a ton of different installation +options. Removing the CGO requirement has massively simplified setup, as well as making debugging an awful lot easier. +Whilst I have put a fair bit of effort in getting this working, the majority of the +credit should go to [John Chadwick](https://github.com/jchv) for not only starting a couple of projects to make this +possible, but also being open to someone taking those projects and building on them. Credit also to +[Tad Vizbaras](https://github.com/tadvi) whose [winc](https://github.com/tadvi/winc) project started me down this path. + +### WebView2 Chromium Renderer + +
+ +
+
+ +Finally, Windows developers get a first class rendering engine for their applications! Gone are the days of contorting +your frontend code to work on Windows. On top of that, you get a first-class developer tools experience! + +The WebView2 component does, however, have a requirement to have the `WebView2Loader.dll` sitting alongside the binary. +This makes distribution just that little bit more painful than we gophers are used to. All solutions and libraries +(that I know of) that use WebView2 have this dependency. + +However, I'm really excited to announce that Wails applications *have no such requirement*! Thanks to the wizardry of +[John Chadwick](https://github.com/jchv), we are able to bundle this dll inside the binary and get Windows to load it +as if it were present on disk. + +Gophers rejoice! The single binary dream lives on! + +### New Features + +
+ +
+
+ +There were a lot of requests for native menu support. Wails has finally got you covered. Application menus are now available +and include support for most native menu features. This includes standard menu items, checkboxes, radio groups, submenus +and separators. + +There were a huge number of requests in v1 for the ability to have greater control of the window itself. +I'm happy to announce that there's new runtime APIs specifically for this. +It's feature-rich and supports multi-monitor configurations. There is also an improved dialogs API: Now, you can have modern, native +dialogs with rich configuration to cater for all your dialog needs. + +There is now the option to generate IDE configuration along with your project. This means that if you open your project +in a supported IDE, it will already be configured for building and debugging your application. Currently VSCode is supported +but we hope to support other IDEs such as Goland soon. + +
+ +
+
+ +### No requirement to bundle assets + +A huge pain-point of v1 was the need to condense your entire application down to single JS & CSS files. I'm happy to +announce that for v2, there is no requirement to bundle assets, in any way, shape or form. Want to load a local image? Use an +`` tag with a local src path. Want to use a cool font? Copy it in and add the path to it in your CSS. + +> Wow, that sounds like a webserver... + +Yes, it works just like a webserver, except it isn't. + +> So how do I include my assets? + +You just pass a single `embed.FS` that contains all your assets into your application configuration. +They don't even need to be in the top directory - Wails will just work it out for you. + +### New Development Experience + +
+ +
+
+ + +Now that assets don't need to be bundled, it's enabled a whole new development experience. The new `wails dev` +command will build and run your application, but instead of using the assets in the `embed.FS`, it loads them directly +from disk. + +It also provides the additional features: + + - Hot reload - Any changes to frontend assets will trigger and auto reload of the application frontend + - Auto rebuild - Any changes to your Go code will rebuild and relaunch your application + +In addition to this, a webserver will start on port 34115. This will serve your application to any browser that +connects to it. All connected web browsers will respond to system events like hot reload on asset change. + +In Go, we are used to dealing with structs in our applications. It's often useful to send structs to our frontend +and use them as state in our application. In v1, this was a very manual process and a bit of a burden on the +developer. I'm happy to announce that in v2, any application run in dev mode will automatically generate Typescript +models for all structs that are input or output parameters to bound methods. This enables seamless interchange of data +models between the two worlds. + +In addition to this, another JS module is dynamically generated wrapping all your bound methods. This provides +JSDoc for your methods, providing code completion and hinting in your IDE. It's really cool when you get data models +auto-imported when hitting tab in an auto-generated module wrapping your Go code! + +### Remote Templates + +
+ +
+
+ +Getting an application up and running quickly was always a key goal for the Wails project. When we launched, we tried +to cover a lot of the modern frameworks at the time: react, vue and angular. The world of frontend development is very +opinionated, fast moving and hard to keep on top of! As a result, we found our base templates getting out of date pretty +quickly and this caused a maintenance headache. It also meant that we didn't have cool modern templates for the latest +and greatest tech stacks. + +With v2, I wanted to empower the community by giving you the ability to create and host templates yourselves, rather +than rely on the Wails project. So now you can create projects using community supported templates! I hope this will +inspire developers to create a vibrant ecosystem of project templates. I'm really quite excited about what our developer +community can create! + +### In Conclusion + +Wails v2 represents a new foundation for the project. +The aim of this release is to get feedback on the new approach, and to iron out any bugs before a full release. +Your input would be most welcome. Please direct any feedback to the [v2 Beta](https://github.com/wailsapp/wails/discussions/828) +discussion board. + +There were many twists and turns, pivots and u-turns to get to this point. This was due partly to early technical decisions +that needed changing, and partly because some core problems we had spent time building workarounds for were fixed upstream: +Go’s embed feature is a good example. Fortunately, everything came together at the right time, and today we have the +very best solution that we can have. I believe the wait has been worth it - this would not have been possible even 2 +months ago. + +I also need to give a huge thank you :pray: to the following people because without them, this release just wouldn't exist: + +- [Misitebao](https://github.com/misitebao) - An absolute workhorse on the Chinese translations and an incredible bug finder. +- [John Chadwick](https://github.com/jchv) - His amazing work on [go-webview2](https://github.com/jchv/go-webview2) and + [go-winloader](https://github.com/jchv/go-winloader) have made the Windows version we have today possible. +- [Tad Vizbaras](https://github.com/tadvi) - Experimenting with his [winc](https://github.com/tadvi/winc) project was the first step down the path to a pure Go Wails. +- [Mat Ryer](https://github.com/matryer) - His support, encouragement and feedback has really helped drive the project forward. + +And finally, I'd like to give a special thank you to all the [project sponsors](/docs/credits#sponsors), including [JetBrains](https://www.jetbrains.com?from=Wails), +whose support drive the project in many ways behind the scenes. + +I look forward to seeing what people build with Wails in this next exciting phase of the project! + +Lea. + +PS: MacOS and Linux users need not feel left out - porting to this new foundation is actively under way and most of the hard work has already been done. Hang in there! + +PPS: If you or your company find Wails useful, please consider [sponsoring the project](https://github.com/sponsors/leaanthony). Thanks! diff --git a/website/blog/authors.yml b/website/blog/authors.yml new file mode 100644 index 000000000..8ddd3c599 --- /dev/null +++ b/website/blog/authors.yml @@ -0,0 +1,11 @@ +leaanthony: + name: Lea Anthony + title: Maintainer of Wails + url: https://github.com/leaanthony + image_url: https://github.com/leaanthony.png + +misitebao: + name: Misitebao + title: Front-End Architect + url: https://github.com/misitebao + image_url: https://cdn.jsdelivr.net/gh/misitebao/CDN@main/gravatar.gif diff --git a/website/docs/about.md b/website/docs/about.md new file mode 100644 index 000000000..856df1046 --- /dev/null +++ b/website/docs/about.md @@ -0,0 +1,70 @@ +--- +sidebar_position: 1 +--- + +# About + +## Overview + +Wails is a project that enables you to write desktop apps using Go and web technologies. + +Consider it a lightweight and fast Electron alternative for Go. You can easily build applications with the flexibility +and power of Go, combined with a rich, modern frontend. + +Wails doesn't hold back with the eye candy either! This is [xbar](https://xbarapp.com) - a desktop application for MacOS +written using Wails. It has menus, supports light and dark desktop themes, and the main window uses translucency that +gives it that 'frosty' effect of a native app. + +

+ +

+ +## Native Elements + +Wails uses a purpose built library for handling native elements such as Window, Menus, Dialogs, etc, so you can build +good-looking, feature rich desktop applications. + +**It does not embed a browser**, so it is resource efficient. Instead, it uses the native rendering engine for the +platform. On Windows, this is the new Microsoft Webview2 library, built on Chromium. + +## Go & Javascript Interoperability + +Wails automatically makes your Go methods available to Javascript, so you can call them by name from your frontend! +It even generates Typescript versions of the structs used by your Go methods, so you can pass the same data structures +between Go and Javascript. + +## Runtime Library + +Wails provides a runtime library, for both Go and Javascript, that handles a lot of the things modern applications need, +like Eventing, Logging, Dialogs, etc. + +## Live Development Experience + +### Automatic Rebuilds + +When you run your application in "dev" mode, Wails will build your application as a native desktop application, but will +read your assets from disk. It will detect any changes to your Go code and automatically rebuild and relaunch your +application. + +### Automatic Reloads + +When changes to your application assets are detected, your running application will "reload", reflecting your changes +almost immediately. + +### Develop your application in a Browser + +If you prefer to debug and develop in a browser then Wails has you covered. The running application also has a webserver +that will run your application in any browser that connects to it. It will even refresh when your assets change on disk. + +## Production-ready Native Binaries + +When you're ready to do the final build of your application, the CLI will compile it down to a single executable, with +all the assets bundled into it. On Windows and MacOS, it is possible to create a native package for distribution. The +assets used in packaging (icon, info.plist, manifest file, etc) are part of your project and may be customised, giving +you total control over how your applications are built. + +## Tooling + +The Wails CLI provides a hassle-free way to generate, build and bundle your applications. It will do the heavy lifting +of creating icons, compiling your application with optimal settings and delivering a distributable, production ready +binary. Choose from a number of starter templates to get up and running quickly! diff --git a/website/docs/community/_category_.json b/website/docs/community/_category_.json new file mode 100644 index 000000000..524986e1e --- /dev/null +++ b/website/docs/community/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Community", + "position": 50 +} diff --git a/website/docs/community/links.mdx b/website/docs/community/links.mdx new file mode 100644 index 000000000..520a428c7 --- /dev/null +++ b/website/docs/community/links.mdx @@ -0,0 +1,19 @@ +--- +sidebar_position: 2 +--- + +# Links + +This page serves as a list for community related links. Please submit a PR (click `Edit this page` at the bottom) +to submit links. + +## Support Channels + + - [Gophers Slack Channel](https://gophers.slack.com/messages/CJ4P9F7MZ/) + - [Gophers Slack Channel Invite](https://invite.slack.golangbridge.org/) + - [Github Issues](https://github.com/wailsapp/wails/issues) + - [v2 Beta Discussion Board](https://github.com/wailsapp/wails/discussions/828) + +## Social Media + + - [Twitter](https://twitter.com/wailsapp) \ No newline at end of file diff --git a/website/docs/community/showcase/_category_.json b/website/docs/community/showcase/_category_.json new file mode 100644 index 000000000..276e283b7 --- /dev/null +++ b/website/docs/community/showcase/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Showcase", + "position": 1 +} diff --git a/website/docs/community/showcase/encrypteasy.mdx b/website/docs/community/showcase/encrypteasy.mdx new file mode 100644 index 000000000..31d7b6ad5 --- /dev/null +++ b/website/docs/community/showcase/encrypteasy.mdx @@ -0,0 +1,10 @@ + +# EncryptEasy + +

+
+

+ +**[EncryptEasy](https://www.encrypteasy.app) is a simple and easy to use PGP encryption tool, managing all your and your contacts keys. Encryption should be simple. Developed with Wails.** + +Encrypting messages using PGP is the industry standard. Everyone has a private and a public key. Your private key, well, needs to be kept private so only you can read messages. Your public key is distributed to anyone who wants to send you secret, encrypted messages. Managing keys, encrypting messages and decrypting messages should be a smooth experience. EncryptEasy is all about making it easy. diff --git a/website/docs/community/showcase/filehound.mdx b/website/docs/community/showcase/filehound.mdx new file mode 100644 index 000000000..687be3870 --- /dev/null +++ b/website/docs/community/showcase/filehound.mdx @@ -0,0 +1,23 @@ + +# FileHound Export Utility + +

+
+

+ + +[FileHound Export Utility](https://www.filehound.co.uk/) FileHound is a cloud document management platform made for secure file retention, business process automation and SmartCapture capabilities. + +The FileHound Export Utility allows FileHound Administrators the ability to run a secure document and data extraction tasks for alternative back-up and recovery purposes. This application will download all documents and/or meta data saved in FileHound based on the filters you choose. The metadata will be exported in both JSON and XML formats. + +Backend built with: +Go 1.15 +Wails 1.11.0 +go-sqlite3 1.14.6 +go-linq 3.2 + +Frontend with: +Vue 2.6.11 +Vuex 3.4.0 +Typescript +Tailwind 1.9.6 diff --git a/website/docs/community/showcase/mollywallet.mdx b/website/docs/community/showcase/mollywallet.mdx new file mode 100644 index 000000000..b75238028 --- /dev/null +++ b/website/docs/community/showcase/mollywallet.mdx @@ -0,0 +1,9 @@ + +# Molley Wallet + +

+
+

+ +[Molly Wallet](https://github.com/grvlle/constellation_wallet/) the official $DAG wallet of the Constellation Network. It'll let users interact with the Hypergraph Network in various ways, not limited to producing $DAG transactions. + diff --git a/website/docs/community/showcase/optimus.mdx b/website/docs/community/showcase/optimus.mdx new file mode 100644 index 000000000..36cbf2fdf --- /dev/null +++ b/website/docs/community/showcase/optimus.mdx @@ -0,0 +1,9 @@ + +# Optimus + +

+
+

+ +[Optimus](https://github.com/splode/optimus) is a desktop image optimization application. It supports conversion and compression between WebP, JPEG, and PNG image formats. + diff --git a/website/docs/community/showcase/portfall.mdx b/website/docs/community/showcase/portfall.mdx new file mode 100644 index 000000000..211009424 --- /dev/null +++ b/website/docs/community/showcase/portfall.mdx @@ -0,0 +1,9 @@ + +# Portfall + +

+
+

+ +[Portfall](https://github.com/rekon-oss/portfall) - A desktop k8s port-forwarding portal for easy access to all your cluster UIs + diff --git a/website/docs/community/showcase/surge.mdx b/website/docs/community/showcase/surge.mdx new file mode 100644 index 000000000..774e86e70 --- /dev/null +++ b/website/docs/community/showcase/surge.mdx @@ -0,0 +1,9 @@ + +# Surge + +

+
+

+ +[Surge](https://surge.rule110.io/) is a p2p filesharing app designed to utilize blockchain technologies to enable 100% anonymous file transfers. Surge is end-to-end encrypted, decentralized and open source. + diff --git a/website/docs/community/showcase/wally.mdx b/website/docs/community/showcase/wally.mdx new file mode 100644 index 000000000..3e842af40 --- /dev/null +++ b/website/docs/community/showcase/wally.mdx @@ -0,0 +1,9 @@ + +# Wally + +

+
+

+ +[Wally](https://ergodox-ez.com/pages/wally) is the official firmware flasher for [Ergodox](https://ergodox-ez.com/) keyboards. It looks great and is a fantastic example of what you can achieve with Wails: the ability to combine the power of Go and the rich graphical tools of the web development world. + diff --git a/website/docs/community/showcase/wombat.mdx b/website/docs/community/showcase/wombat.mdx new file mode 100644 index 000000000..8fdbe7cd8 --- /dev/null +++ b/website/docs/community/showcase/wombat.mdx @@ -0,0 +1,10 @@ + +# Wombat + +

+
+

+ + +[Wombat](https://github.com/rogchap/wombat) is a cross platform gRPC client. + diff --git a/website/docs/community/templates.mdx b/website/docs/community/templates.mdx new file mode 100644 index 000000000..3939c5c2d --- /dev/null +++ b/website/docs/community/templates.mdx @@ -0,0 +1,30 @@ +--- +sidebar_position: 1 +--- + +# Templates + +This page serves as a list for community supported templates. Please submit a PR (click `Edit this page` at the bottom) +to include your templates. To build your own template, please see the [Templates](/docs/guides/templates) guide. + +To use these templates, run `wails init -n "Your Project Name" -t [the link below]` + +Example: `wails init -n "Your Project Name" -t https://github.com/misitebao/wails-template-vue` + +:::warning Attention + + **The Wails project does not maintain, is not responsible nor liable for 3rd party templates!** + + If you are unsure about a template, inspect `package.json` and `wails.json` for what scripts are run and what packages are installed. + +::: + +## Vue + +- [wails-template-vue](https://github.com/misitebao/wails-template-vue) - A template using vue and vue-router +- [wails-vite-vue-ts](https://github.com/codydbentley/wails-vite-vue-ts) - Vue 3 TypeScript with Vite (and instructions to add features) +- [wails-vite-vue-the-works](https://github.com/codydbentley/wails-vite-vue-the-works) - Vue 3 TypeScript with Vite, Vuex, Vue Router, Sass, and ESLint + Prettier + +## Angular + +- [wails-angular-template](https://github.com/TAINCER/wails-angular-template) - Angular with TypeScript, Sass, Hot-Reload, Code-Splitting and i18n diff --git a/website/docs/credits.mdx b/website/docs/credits.mdx new file mode 100644 index 000000000..896cdceac --- /dev/null +++ b/website/docs/credits.mdx @@ -0,0 +1,104 @@ +--- +sidebar_position: 99 +--- + +# Credits + +- [Lea Anthony](https://github.com/leaanthony) - Project owner, lead developer +- [Misitebao](https://github.com/misitebao) - Chinese documentation, Windows testing, Bug finder general +- [Travis McLane](https://github.com/tmclane) - Cross-compilation work, MacOS testing +- [Byron Chris](https://github.com/bh90210) - Linux distro wizard, Linux testing + +## Sponsors + +
+ + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`, + }} +/> + +## Contributors + +import Contributors from "react-contributors"; + + + +## Special Mentions + +- [John Chadwick](https://github.com/jchv) - His amazing work on [go-webview2](https://github.com/jchv/go-webview2) and + [go-winloader](https://github.com/jchv/go-winloader) have made the Windows version possible. +- [Tad Vizbaras](https://github.com/tadvi) - His winc project was the first step down the path to a pure Go Wails. +- [Mat Ryer](https://github.com/matryer) - For advice, support and bants. +- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and feedback has been invaluable. +- [Justen Walker](https://github.com/justenwalker/) - For helping wrangle COM issues which got v2 over the line. +- [Wang, Chi](https://github.com/patr0nus/) - The DeskGap project was a huge influence on the direction of Wails v2. +- [Serge Zaitsev](https://github.com/zserge) - Whilst Wails does not use the Webview project, it is still a source of inspiration. diff --git a/website/docs/gettingstarted/_category_.json b/website/docs/gettingstarted/_category_.json new file mode 100644 index 000000000..597b920df --- /dev/null +++ b/website/docs/gettingstarted/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Getting Started", + "position": 10 +} diff --git a/website/docs/gettingstarted/building.mdx b/website/docs/gettingstarted/building.mdx new file mode 100644 index 000000000..e8f2e9e76 --- /dev/null +++ b/website/docs/gettingstarted/building.mdx @@ -0,0 +1,19 @@ +--- +sidebar_position: 6 +--- + +# Compiling your Project + +From the project directory, run `wails build`. +This will compile your project and save the production-ready binary in the `build/bin` directory. + +If you run the binary, you should see the default application: + +
+ +
+
+ + +For more details on compilation options, please refer to the [CLI Reference](/docs/reference/cli#build). + diff --git a/website/docs/gettingstarted/development.mdx b/website/docs/gettingstarted/development.mdx new file mode 100644 index 000000000..5e2977f34 --- /dev/null +++ b/website/docs/gettingstarted/development.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 5 +--- + +# Developing your Application + +You can run your application in development mode by running `wails dev` from your project directory. This will do the following things: + + - Build your application and run it + - Watch for modifications in your Go files and rebuild/re-run on change + - Sets up a [webserver](http://localhost:34115) that will serve your application over a browser. This allows you to use your favourite browser extensions. You can even call your Go code from the console. + +To get started, run `wails dev` in the project directory. More information on this can be found [here](/docs/reference/cli#dev). + +Coming soon: Tutorial \ No newline at end of file diff --git a/website/docs/gettingstarted/firstproject.mdx b/website/docs/gettingstarted/firstproject.mdx new file mode 100644 index 000000000..014e04caa --- /dev/null +++ b/website/docs/gettingstarted/firstproject.mdx @@ -0,0 +1,53 @@ +--- +sidebar_position: 2 +--- + +# Creating a Project + +## Project Generation + +Now that the CLI is installed, you can generate a new project by using the `wails init` command. + +To get up and running quickly, you can generate a default project by running `wails init -n myproject`. This will +create a directory called `myproject` and populate it with the default template. + +Other project templates are available and can be listed using `wails init -l`. + +To see the other options available, you can run `wails init -help`. +More details can be found in the [CLI Reference](/docs/reference/cli#init). + +## Project Layout + +Wails projects have the following layout: + +``` +. +├── build/ +│ ├── appicon.png +│ ├── darwin/ +│ └── windows/ +├── frontend/ +├── go.mod +├── go.sum +├── main.go +└── wails.json +``` + +### Project structure rundown + +- `/main.go` - The main application +- `/frontend/` - Frontend project files +- `/build/` - Project build directory + - `/build/appicon.png` - The application icon + - `/build/darwin/` - Mac specific project files + - `/build/windows/` - Windows specific project files +- `/wails.json` - The project configuration +- `/go.mod` - Go module file +- `/go.sum` - Go module checksum file + +The `frontend` directory has nothing specific to Wails and can be any frontend project of your choosing. + +The `build` directory is used during the build process. These files may be updated to customise your builds. If +files are removed from the build directory, default versions will be regenerated. + +The default module name in `go.mod` is "changeme". You should change this to something more appropriate. diff --git a/website/docs/gettingstarted/installation.mdx b/website/docs/gettingstarted/installation.mdx new file mode 100644 index 000000000..5cd767d95 --- /dev/null +++ b/website/docs/gettingstarted/installation.mdx @@ -0,0 +1,69 @@ +--- +sidebar_position: 1 +--- + +# Installation + +## Supported Platforms + +- Windows 10 +- MacOS x64 & arm64 (due October '21) +- Linux (due December '21) + +## Dependencies + +Wails has a number of common dependencies that are required before installation: + +- Go 1.17+ +- npm (Node 14+) + +### Go + +Download Go from the [Go Downloads Page](https://golang.org/dl/). + +Ensure that you follow the official [Go installation instructions](https://golang.org/doc/install#install). You will also need to ensure that your `PATH` environment variable also includes the path to your `~/go/bin` directory. Restart your terminal and do the following checks: + +- Check Go is installed correctly: `go version` +- Check "~/go/bin" is in your PATH variable: `echo $PATH | grep go/bin` + +### npm + +Download NPM from the [Node Downloads Page](https://nodejs.org/en/download/). It is best to use the latest release as that is what we generally test against. + +Run `npm --version` to verify. + +## Platform Specific Dependencies + +You will also need to install platform specific dependencies: + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + + + Coming Soon... + + Wails requires that the WebView2{" "} + runtime is installed. Some Windows installations will already have this installed. You can check using the{" "} + wails doctor command (see below). + + Coming Soon... + + +## Optional Dependencies + +- [UPX](https://upx.github.io/) for compressing your applications. + +## Installing Wails + +Run `go install github.com/wailsapp/wails/v2/cmd/wails@v2.0.0-beta.15` to install the Wails CLI. + +## System Check + +Running `wails doctor` will check if you have the correct dependencies installed. If not, it will advise on what is missing and help on how to rectify any problems. diff --git a/website/docs/guides/_category_.json b/website/docs/guides/_category_.json new file mode 100644 index 000000000..5935dad93 --- /dev/null +++ b/website/docs/guides/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Guides", + "position": 50 +} diff --git a/website/docs/guides/application-development.mdx b/website/docs/guides/application-development.mdx new file mode 100644 index 000000000..46f088597 --- /dev/null +++ b/website/docs/guides/application-development.mdx @@ -0,0 +1,188 @@ + +# Application Development + +There are no hard and fast rules for developing applications with Wails, but there are some basic guidelines. + +## Application Setup + +The pattern used by the default templates are that `main.go` is used for configuring and running the application, whilst +`app.go` is used for defining the application logic. + +The `app.go` file will define a struct that has 2 methods which act as hooks into the main application: + +```go title="app.go" +type App struct { + ctx context.Context +} + +func NewApp() *App { + return &App{} +} + +func (a *App) startup(ctx context.Context) { + a.ctx = ctx +} + +func (a *App) shutdown(ctx context.Context) { +} +``` + +- The startup method is called as soon as Wails allocates the resources it needs and is a good place for creating resources, + setting up event listeners and anything else the application needs at startup. + It is given a `context.Context` which is usually saved in a struct field. This context is needed for calling the + [runtime](/docs/reference/runtime/intro). If this method returns an error, the application will terminate. + In dev mode, the error will be output to the console. + +- The shutdown method will be called by Wails right at the end of the shutdown process. This is a good place to deallocate + memory and perform any shutdown tasks. + +The `main.go` file generally consists of a single call to `wails.Run()`, which accepts the application configuration. +The pattern used by the templates is that before the call to `wails.Run()`, an instance of the struct we defined in +`app.go` is created and saved in a variable called `app`. This configuration is where we add our callbacks: + +```go {3,9,10} title="main.go" +func main() { + + app := NewApp() + + err := wails.Run(&options.App{ + Title: "My App", + Width: 800, + Height: 600, + OnStartup: app.startup, + OnShutdown: app.shutdown, + }) + if err != nil { + log.Fatal(err) + } +} + +``` + +More information on application lifecycle hooks can be found [here](/docs/howdoesitwork#application-lifecycle-callbacks). + +## Binding Methods + +It is likely that you will want to call Go methods from the frontend. This is normally done by adding public methods to +the already defined struct in `app.go`: + +```go {16-18} title="app.go" +type App struct { + ctx context.Context +} + +func NewApp() *App { + return &App{} +} + +func (a *App) startup(ctx context.Context) { + a.ctx = ctx +} + +func (a *App) shutdown(ctx context.Context) { +} + +func (a *App) Greet(name string) string { + return fmt.Printf("Hello %s!", name) +} +``` + +In the main application configuration, the `Bind` key is where we can tell Wails what we want to bind: + +```go {11-13} title="main.go" +func main() { + + app := NewApp() + + err := wails.Run(&options.App{ + Title: "My App", + Width: 800, + Height: 600, + OnStartup: app.startup, + OnShutdown: app.shutdown, + Bind: []interface{}{ + app, + }, + }) + if err != nil { + log.Fatal(err) + } +} + +``` + +This will bind all public methods in our `App` struct (it will never bind the startup and shutdown methods). + +More information on Binding can be found [here](/docs/howdoesitwork#method-binding). + +## Application Menu + +Wails supports adding a menu to your application. This is done by passing a [Menu](/docs/reference/menus#menu) struct +to application config. It's common to use a method that returns a Menu, and even more common for that to be a method on +the `App` struct used for the lifecycle hooks. + +```go {11} title="main.go" +func main() { + + app := NewApp() + + err := wails.Run(&options.App{ + Title: "My App", + Width: 800, + Height: 600, + OnStartup: app.startup, + OnShutdown: app.shutdown, + Menu: app.menu(), + Bind: []interface{}{ + app, + }, + }) + if err != nil { + log.Fatal(err) + } +} + +``` + +## Assets + +The great thing about the way Wails v2 handles assets is that it doesn't! The only thing you need to give Wails is an +`embed.FS`. How you get to that is entirely up to you. You can use vanilla html/css/js files like the vanilla template. +You could have some complicated build system, it doesn't matter. + +When `wails build` is run, it will check the `wails.json` project file at the project root. There are 2 keys in the +project file that are read: + +- "frontend:install" +- "frontend:build" + +The first, if given, will be executed in the `frontend` directory to install the node modules. +The second, if given, will be executed in the `frontend` directory to build the frontend project. + +If these 2 keys aren't given, then Wails does absolutely nothing with the frontend. It is only expecting that `embed.FS`. + +## Built in Dev Server + +Running `wails dev` will start the built in dev server which will start a file watcher in your project directory. By +default, if any file changes, wails checks if it was an application file (default: `.go`, configurable with `-e` flag). +If it was, then it will rebuild your application and relaunch it. If the changed file was in the `assetdir` directory, +it will issue a reload after a short amount of time. + +The dev server uses a technique called "debouncing" which means it doesn't reload straight away, +as there may be multiple files changed in a short amount of time. When a trigger occurs, it waits for a set amount of time +before issuing a reload. If another trigger happens, it resets to the wait time again. By default this value is `100ms`. +If this value doesn't work for your project, it can be configured using the `-debounce` flag. If used, this value will +be saved to your project config and become the default. + +## External Dev Server + +Some frameworks come with their own live-reloading server, however they will not be able to take advantage of the Wails +Go bindings. In this scenario, it is best to run a watcher script that rebuilds the project into the build directory, which +Wails will be watching. For an example, see the default svelte template that uses [rollup](https://rollupjs.org/guide/en/). +For [create-react-app](https://create-react-app.dev/), it's possible to use +[this script](https://gist.github.com/int128/e0cdec598c5b3db728ff35758abdbafd) to achieve a similar result. + +## Go Module + +The default Wails templates generate a `go.mod` file that contains the module name "changeme". You should change this +to something more appropriate after project generation. diff --git a/website/docs/guides/developing-wails.mdx b/website/docs/guides/developing-wails.mdx new file mode 100644 index 000000000..58f9e9bd8 --- /dev/null +++ b/website/docs/guides/developing-wails.mdx @@ -0,0 +1,39 @@ + +# Contributing + +This page is a guide on how to contribute to the Wails project. + +First, a word of warning: Wails v2 has been through a number of iterations and pivots. There is a lot of code that +is either on hold or deprecated. Reading the whole project and trying to understand it may be confusing. This document +aims to focus on what is current and how to understand that. + +## Bugs + +For raising bugs, please open a ticket on GitHub and give it the \[v2\] label. Include the output of `wails doctor` +in the ticket to help us understand your environment. + +For fixing bugs, please comment on a ticket that you'd like to take it on and we will put a label on the ticket. +It is best to use Windows as it is done in pure Go, making debugging much easier. + +## Features + +To request a new feature, raise a ticket so that it may be discussed. The ticket should be given the +"Feature Request" label. These will be discussed and if selected for development will be given the label +"Ready for Development". + +To implement a new feature, raise a ticket as above or select a ticket with the "Ready for Development" label. + +When raising a PR, be mindful to state what platforms the PR has been tested on. Any new feature will not be accepted unless it works +on all platforms (if it can). + +:::warning What not to do + +PRs for features with no tickets aren't helpful as there's no context to the PR and it will not be prioritised. + +::: + +## Documentation + +Contributing to the documentation is easy by clicking on the "Edit this page" link on any of the pages. Documentation +updates can be done ad-hoc, without a ticket. + diff --git a/website/docs/guides/frameless.mdx b/website/docs/guides/frameless.mdx new file mode 100644 index 000000000..3bd48ca3a --- /dev/null +++ b/website/docs/guides/frameless.mdx @@ -0,0 +1,35 @@ + +# Frameless Applications + +Wails supports applications with no frame. This can be achieved by using the [frameless](/docs/reference/options#frameless) +field in [Application Options](/docs/reference/options#application-options). + +Wails offers a simple solution for dragging the window: Any HTML element that has the attribute "data-wails-drag" will +act as a "drag handle". This property applies to all nested elements. If you need to indicate that a nested element +should not drag, then use the attribute 'data-wails-no-drag' on that element. + +The default vanilla template uses this, even though it is not frameless. The whole `body` element is tagged as draggable. +The `
` is tagged as being not draggable. + +```html + + + + + + + +
+ + +
+
+ + + + +``` + +:::info Fullscreen + If you allow your application to go fullscreen, this drag functionality will be disabled. +::: diff --git a/website/docs/guides/frontend.mdx b/website/docs/guides/frontend.mdx new file mode 100644 index 000000000..84489cd99 --- /dev/null +++ b/website/docs/guides/frontend.mdx @@ -0,0 +1,77 @@ + +# Frontend + +## Script Injection + +When Wails serves your `index.html`, by default, it will inject 2 script entries into the `` tag to load `/wails/bindings.js` +and `/wails/runtime.js`. These files install the bindings and runtime respectively. + +The code below shows where these are injected by default: + +```html + + + injection example + + + + + + + +
Please enter your name below 👇
+
+ + +
+ + + + + +``` + +### Overriding Default Script Injection + +To provide more flexibility to developers, there is a meta tag that may be used to customise this behaviour: + +```html + +``` + +The options are as follows: + +| Value | Description | +| -------------------- | ------------------------------------------------- | +| noautoinjectruntime | Disable the autoinjection of `/wails/runtime.js` | +| noautoinjectipc | Disable the autoinjection of `/wails/ipc.js` | +| noautoinject | Disable all autoinjection of scripts | + +Multiple options may be used provided they are comma seperated. + +This code is perfectly valid and operates the same as the autoinjection version: + +```html + + + + injection example + + + + + + +
Please enter your name below 👇
+
+ + +
+ + + + + + + +``` \ No newline at end of file diff --git a/website/docs/guides/ides.mdx b/website/docs/guides/ides.mdx new file mode 100644 index 000000000..fda70ebf6 --- /dev/null +++ b/website/docs/guides/ides.mdx @@ -0,0 +1,116 @@ + +# IDEs + +Wails aims to provide a great development experience. To that aim, we now support generating IDE specific configuration +to provide smoother project setup. + +Currently, we support [Visual Studio Code](https://code.visualstudio.com/) but aim to support other IDEs such as Goland. + +## Visual Studio Code + +

+ +

+ +When generating a project using the `-ide vscode` flags, IDE files will be created alongside the other project files. +These files are placed into the `.vscode` directory and provide the correct configuration for debugging your application. + +The 2 files generated are `tasks.json` and `launch.json`. Below are the files generated for the default vanilla project: + +```json title="tasks.json" +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "command": "go", + "args": ["build", "-tags", "dev", "-gcflags", "all=-N -l", "-o", "build/bin/myproject.exe"] + }, + ] +} +``` + +```json title="launch.json" +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Wails: Debug myproject", + "type": "go", + "request": "launch", + "mode": "exec", + "program": "${workspaceFolder}/build/bin/myproject.exe", + "preLaunchTask": "build", + "cwd": "${workspaceFolder}", + "env": {}, + "args": ["-assetdir", "frontend/src"] + }, + ] +} +``` + +### Configuring the install and build steps + +The `tasks.json` file is simple for the default project as there is no `npm install` or `npm run build` step needed. +For projects that have a frontend build step, such as the svelte template, we would need to edit `tasks.json` to +add the install and build steps: + +```json title="tasks.json" +{ + "version": "2.0.0", + "tasks": [ + { + "label": "npm install", + "type": "npm", + "script": "install", + "options": { + "cwd": "${workspaceFolder}/frontend" + }, + "presentation": { + "clear": true, + "panel": "shared", + "showReuseMessage": false + }, + "problemMatcher": [] + }, + { + "label": "npm run build", + "type": "npm", + "script": "build", + "options": { + "cwd": "${workspaceFolder}/frontend" + }, + "presentation": { + "clear": true, + "panel": "shared", + "showReuseMessage": false + }, + "problemMatcher": [] + }, + { + "label": "build", + "type": "shell", + "options": { + "cwd": "${workspaceFolder}" + }, + "command": "go", + "args": ["build", "-tags", "dev", "-gcflags", "all=-N -l", "-o", "build/bin/vscode.exe"], + "dependsOn":[ + "npm install", + "npm run build" + ] + + }, + ] +} +``` + +:::info Future Enhancement + +In the future, we hope to generate a `tasks.json` that includes the install and build steps automatically. + +::: \ No newline at end of file diff --git a/website/docs/guides/manual-builds.mdx b/website/docs/guides/manual-builds.mdx new file mode 100644 index 000000000..9d525314b --- /dev/null +++ b/website/docs/guides/manual-builds.mdx @@ -0,0 +1,99 @@ + +# Manual builds + +The Wails CLI does a lot of heavy lifting for the project, but sometimes it's desirable to manually build your project. +This document will discuss the different operations the CLI does and how this may be achieved in different ways. + +## Build Process + +When either `wails build` or `wails dev` are used, the Wails CLI performs a common build process: + + - Install frontend dependencies + - Build frontend project + - Generate build assets + - Compile application + - [optional] Compress application + +### Install frontend dependencies + +#### CLI Steps + +- If the `-s` flag is given, this step is skipped +- Checks `wails.json` to see if there is an install command in the key `frontend:install` +- If there isn't, it skips this step +- If there is, it checks if `package.json` exists in the frontend directory. If it doesn't exist, it skips this step +- An MD5 sum is generated from the `package.json` file contents +- It checks for the existence of `package.json.md5` and if it exists, will compare the contents of it (an MD5 sum) + with the one generated to see if the contents have changed. If they are the same, this step is skipped +- If `package.json.md5` does not exist, it creates it using the generated MD5 sum +- If a build is now required, or `node_modules` does not exist, or the `-f` flag is given, the install command is + executed in the frontend directory + +#### Manual Steps + +This step could be done from the command line or a script with `npm install`. + +### Build frontend project + +#### Wails CLI + +- If the `-s` flag is given, this step is skipped +- Checks `wails.json` to see if there is a build command in the key `frontend:build` +- If there isn't, it skips this step +- If there is, it is executed in the frontend directory + +#### Manual Steps + +This step could be done from the command line or a script with `npm run build` or whatever the frontend build script is. + +### Generate assets + +#### Wails CLI + +- If `-nopackage` flag is set, this stage is skipped +- If the `build/appicon.png` file does not exist, a default one is created +- For Windows, see [Bundling for Windows](#windows) +- If `build/windows/icon.ico` does not exist, it will create it from the `build/appicon.png` image. + +##### Windows + +- It creates icon sizes of 256, 128, 64, 48, 32 and 16. This is done using [winicon](https://github.com/leaanthony/winicon). +- If the `build/windows/.manifest` file does not exist, it creates it from a default version. +- Compiles the application as a production build (above) +- Uses [winres](https://github.com/tc-hib/winres) to bundle the icon and manifest into a `.syso` file ready for linking. + +#### Manual Steps + +- Create `icon.ico` using the [winicon](https://github.com/leaanthony/winicon) CLI tool (or any other tool). +- Create / Update a `.manifest` file for your application +- Use the [winres CLI](https://github.com/tc-hib/go-winres) to generate a `.syso` file. + +### Compile application + +#### Wails CLI + +- If the `-clean` flag is provided, the `build` directory is deleted and recreated +- For `wails dev`, the following default Go flags are used: `-tags dev -gcflags "all=-N -l"` +- For `wails build`, the following default Go flags are used: `-tags desktop,production -ldflags "-w -s"` + - On Windows, `-ldflags "-w -h -H windowsgui"` +- Additional tags passed to the CLI using `-tags` are added to the defaults +- Additional ldflags passed to the CLI using `-ldflags` are added to the defaults +- The `-o` flag is passed through +- The Go compiler specified by `-compiler` will be used for compilation + +#### Manual steps + +- For dev build, the minimum command would be: `go build -tags dev -gcflags "all=-N -l"` +- For production build, the minimum command would be: `go build -tags desktop,production -ldflags "-w -s -H windowsgui"` +- Ensure that you compile in the same directory as the `.syso` file + +### Compress application + +#### Wails CLI + +- If the `-upx` flag has been given, the `upx` program will be run to compress the application with the default settings +- If `-upxflags` is also passed, these flags are used instead of the default ones + +#### Manual steps + +- Run `upx [flags]` manually to compress the application. diff --git a/website/docs/guides/migrating.mdx b/website/docs/guides/migrating.mdx new file mode 100644 index 000000000..e9800c92a --- /dev/null +++ b/website/docs/guides/migrating.mdx @@ -0,0 +1,205 @@ + +# Migrating from v1 + +## Overview + +Wails v2 is a significant change from v1. This document aims to highlight the changes and the steps in migrating an existing project. + +### Creating the Application + +In v1, the main application is created using `wails.CreateApp`, bindings are added with `app.Bind`, then the +application is run using `app.Run()`. + +Example: + +```go title="v1" + app := wails.CreateApp(&wails.AppConfig{ + Title: "MyApp", + Width: 1024, + Height: 768, + JS: js, + CSS: css, + Colour: "#131313", + }) + app.Bind(basic) + app.Run() +``` + +In v2, there is just a single method, `wails.Run()`, that accepts [application options](/docs/reference/options#application-options). + +```go title="v2" + err := wails.Run(&options.App{ + Title: "MyApp", + Width: 800, + Height: 600, + Assets: assets, + Bind: []interface{}{ + basic, + }, + }) +``` + +### Binding + +In v1, it was possible to bind both arbitrary functions and structs. In v2, this has been simplified to only binding structs. +The struct instances that were previously passed to the `Bind()` method in v1, are now specified in the `Bind` field of +the [application options](/docs/reference/options#application-options): + +```go title="v1" + app := wails.CreateApp(/* options */) + app.Bind(basic) +``` + +```go title="v2" + err := wails.Run(&options.App{ + /* other options */ + Bind: []interface{}{ + basic, + }, + }) +``` + +In v1, bound methods were available to the frontend at `window.backend`. This has changed to `window.go`.`` + +### Application Lifecycle + +In v1, there were 2 special methods in a bound struct: `WailsInit()` and `WailsShutdown()`. These have +been replaced with 3 lifecycle hooks as part of the [application options](/docs/reference/options#application-options): + +- [OnStartup](/docs/reference/options#onstartup) +- [OnShutdown](/docs/reference/options#onshutdown) +- [OnDomReady](/docs/reference/options#ondomready) + +Note: [OnDomReady](/docs/reference/options#ondomready) replaces the `wails:ready` system event in v1. + +These methods can be standard functions, but a common practice is to have them part of a struct: + +```go title="v2" + basic := NewBasicApp() + err := wails.Run(&options.App{ + /* Other Options */ + OnStartup: basic.startup, + OnShutdown: basic.shutdown, + OnDomReady: basic.domready, + }) +... +type Basic struct { + ctx context.Context +} +func (b *Basic) startup(ctx context.Context) { + b.ctx = ctx +} +... +``` + +### Runtime + +The runtime in v2 is much richer than v1 with support for menus, window manipulation +and better dialogs. The signature of the methods has changed slightly - please refer +the the [Runtime Reference](/docs/reference/runtime/intro). + +In v1, the [runtime](/docs/reference/runtime/intro) was available via a struct passed to `WailsInit()`. +In v2, the runtime has been moved out to its own package. Each method in the runtime takes the +`context.Context` that is passed to the [OnStartup](/docs/reference/options#onstartup) method. + +```go title="Runtime Example" +package main + +import "github.com/wailsapp/wails/v2/pkg/runtime" + +type Basic struct { + ctx context.Context +} + +// startup is called at application startup +func (a *App) startup(ctx context.Context) { + a.ctx = ctx + runtime.LogInfo(ctx, "Application Startup called!") +} + +``` + +### Assets + +The _biggest_ change in v2 is how assets are handled. + +In v1, assets were passed via 2 application options: + +- `JS` - The application's Javascript +- `CSS` - The application's CSS + +This meant that the responsibility of generating a single JS and CSS file was on the +developer. This essentially required the use of complicated packers such as webpack. + +In v2, Wails makes no assumptions about your frontend assets, just like a webserver. +All of your application assets are passed to the application options as an `embed.FS`. + +**This means there is no requirement to bundle your assets, encode images as Base64 or +attempt the dark art of bundler configuration to use custom fonts**. + +At startup, Wails +will scan the given `embed.FS` for `index.html` and use its location as the root path +for all the other application assets - just like a webserver would. + +Example: An application has the following project layout. All final assets are placed in the +`frontend/dist` directory: + +```shell +. +├── build/ +├── frontend/ +│ └── dist/ +│ ├── index.html +│ ├── main.js +│ ├── main.css +│ └── logo.svg +├── main.go +└── wails.json +``` + +Those assets may be used by the application by simply creating an `embed.FS`: + +```go title="Assets Example" +//go:embed frontend/dist +var assets embed.FS + +func main() { + err := wails.Run(&options.App{ + /* Other Options */ + Assets: assets, + }) +} +``` + +Of course, bundlers can be used if you wish to. The only requirement is to pass +the final application assets directory to Wails using an `embed.FS` in the `Assets` +key of the [application options](/docs/reference/options#application-options). + +### Project Configuration + +In v1, the project configuration was stored in the `project.json` file in the project root. +In v2, the project configuration is stored in the `wails.json` file in the project root. + +The format of the file is slightly different. Here is a comparison: + +

+ +| v1 | v2 | Notes | +| ------------------ | ---------------- | --------------------------------------------------- | +| name | name | | +| description | | Removed | +| author / name | author / name | | +| author / email | author / email | | +| version | version | | +| binaryname | outputfilename | Changed | +| frontend / dir | | Removed | +| frontend / install | frontend:install | Changed | +| frontend / build | frontend:build | Changed | +| frontend / bridge | | Removed | +| frontend / serve | | Removed | +| tags | | Removed | +| | wailsjsdir | The directory to generate wailsjs modules | +| | assetdir | The directory of the frontend assets for `dev` mode | + +

+ diff --git a/website/docs/guides/overscroll.mdx b/website/docs/guides/overscroll.mdx new file mode 100644 index 000000000..bbe593bc7 --- /dev/null +++ b/website/docs/guides/overscroll.mdx @@ -0,0 +1,11 @@ + +# Overscroll + +[Overscroll](https://developer.mozilla.org/en-US/docs/Web/CSS/overscroll-behavior) is the "bounce effect" you sometimes +get when you scroll beyond a page's content boundaries. This is common in mobile apps. This can be disabled using CSS: + +```css +body { + overscroll-behavior: none; +} +``` \ No newline at end of file diff --git a/website/docs/guides/templates.mdx b/website/docs/guides/templates.mdx new file mode 100644 index 000000000..9890e8fb9 --- /dev/null +++ b/website/docs/guides/templates.mdx @@ -0,0 +1,95 @@ + +# Templates + +Wails generates projects from pre-created templates. In v1, this was a difficult to maintain set of projects that were +subject to going out of date. In v2, to empower the community, a couple of new features have been added for templates: + +- Ability to generate projects from [Remote Templates](/docs/reference/cli#remote-templates) +- Tooling to help create your own templates + +## Creating Templates + +To create a template, you can use the `wails generate template` command. To generate a default template, run: + +`wails generate template -name mytemplate ` + +This creates the directory "mytemplate" with default files: + +```shell title=mytemplate/ +. +|-- NEXTSTEPS.md +|-- README.md +|-- app.tmpl.go +|-- frontend +| `-- dist +| |-- assets +| | |-- fonts +| | | |-- OFL.txt +| | | `-- nunito-v16-latin-regular.woff2 +| | `-- images +| | `-- logo-dark.svg +| |-- index.html +| |-- main.css +| `-- main.js +|-- go.mod.tmpl +|-- main.tmpl.go +|-- template.json +`-- wails.tmpl.json +``` + +### Template Overview + +The default template consists of the following files and directories: + +| Filename / Dir | Description | +| --------------- | -------------------------------------------- | +| NEXTSTEPS.md | Instructions on how to complete the template | +| README.md | The README published with the template | +| app.tmpl.go | `app.go` template file | +| frontend/ | The directory containing frontend assets | +| go.mod.tmpl | `go.mod` template file | +| main.tmpl.go | `main.go` template file | +| template.json | The template metadata | +| wails.tmpl.json | `wails.json` template file | + +At this point it is advisable to follow the steps in `NEXTSTEPS.md`. + +## Creating a Template from an Existing Project + +It's possible to create a template from an existing frontend project by passing the path to the project when generating +the template. We will now walk through how to create a Vue 3 template: + +- Install the vue cli: `npm install -g @vue/cli` +- Create the default project: `vue create vue3-base` + - Select `Default (Vue 3) ([Vue 3] babel, eslint)` +- After the project has been generated, run: + +```shell +> wails generate template -name wails-vue3-template -frontend .\vue3-base\ +Extracting base template files... +Migrating existing project files to frontend directory... +Updating package.json data... +Renaming package.json -> package.tmpl.json... +Updating package-lock.json data... +Renaming package-lock.json -> package-lock.tmpl.json... +``` + +- The template may now be customised as specified in the `NEXTSTEPS.md` file +- Once the files are ready, it can be tested by running: `wails init -n my-vue3-project -t .\wails-vue3-template\` +- To test the new project, run: `cd my-vue3-project` then `wails build` +- Once the project has compiled, run it: `.\build\bin\my-vue3-project.exe` +- You should have a fully functioning Vue3 application: + +
+ +
+ +## Publishing Templates + +Publishing a template is simply pushing the files to GitHub. The following best practice is encouraged: + +- Remove any unwanted files and directories (such as `.git`) from your frontend directory +- Ensure that `template.json` is complete, especially `helpurl` +- Push the files to GitHub +- Create a PR on the [Community Templates](/docs/community/templates) page +- Announce the template on the [Template Announcement](https://github.com/wailsapp/wails/discussions/825) discussion board diff --git a/website/docs/guides/windows.mdx b/website/docs/guides/windows.mdx new file mode 100644 index 000000000..e0ec17d5f --- /dev/null +++ b/website/docs/guides/windows.mdx @@ -0,0 +1,37 @@ + +# Windows + +This page has miscellaneous guides related to developing Wails applications for Windows. + +## Handling the WebView2 Runtime Dependency + +Wails applications built for Windows have a runtime requirement on the Microsoft [WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/). +Windows 11 will have this installed by default, but some machines won't. Wails offers an easy approach to dealing with this dependency. + +By using the `-webview2` flag when building, you can decide what your application will do when a suitable runtime is not detected (including if the installed runtime is too old). +The four options are: + +1. Download +2. Embed +3. Browser +4. Error + +### Download + +This option will prompt the user that no suitable runtime has been found and then offer to download and run the official +bootstrapper from Microsoft's WebView2 site. If the user proceeds, the official bootstrapper will be downloaded and run. + +### Embed + +This option embeds the official bootstrapper within the application. If no suitable runtime has been found, the +application will offer to run the bootstrapper. This adds ~150k to the binary size. + +### Browser + +This option will prompt the user that no suitable runtime has been found and then offer to open a browser to the official +WebView2 page where the bootstrapper can be downloaded and installed. The application will then exit, leaving the installation +up to the user. + +### Error + +If no suitable runtime is found, an error is given to the user and no further action taken. diff --git a/website/docs/howdoesitwork.mdx b/website/docs/howdoesitwork.mdx new file mode 100644 index 000000000..9db5e8a73 --- /dev/null +++ b/website/docs/howdoesitwork.mdx @@ -0,0 +1,331 @@ +--- +sidebar_position: 20 +--- + +# How does it work? + +A Wails application is a standard Go application, with a webkit frontend. The Go part of the application consists of the +application code and a runtime library that provides a number of useful operations, like controlling the application +window. The frontend is a webkit window that will display the frontend assets. Also available to the frontend is a Javascript +version of the runtime library. Finally, it is possible to bind Go methods to the frontend, and these will appear as +Javascript methods that can be called, just as if they were local Javascript methods. + +
+ +
+ +## The Main Application + +### Overview + +The main application consists of a single call to `wails.Run()`. It accepts the +application configuration which describes the size of the application window, the window title, +what assets to use, etc. A basic application might look like this: + +```go title="main.go" +package main + +import ( + "embed" + "log" + + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/options" +) + +//go:embed frontend/dist +var assets embed.FS + +func main() { + + app := &App{} + + err := wails.Run(&options.App{ + Title: "Basic Demo", + Width: 1024, + Height: 768, + Assets: &assets, + OnStartup: app.startup, + OnShutdown: app.shutdown, + Bind: []interface{}{ + app, + }, + }) + if err != nil { + log.Fatal(err) + } +} + + +type App struct { + ctx context.Context +} + +func (b *App) startup(ctx context.Context) { + b.ctx = ctx +} + +func (b *App) shutdown(ctx context.Context) {} + +func (b *App) Greet(name string) string { + return fmt.Sprintf("Hello %s!", name) +} +``` + +### Options rundown + +This example has the following options set: + +- `Title` - The text that should appear in the window's title bar +- `Width` & `Height` - The dimensions of the window +- `Assets` - The application's frontend assets +- `OnStartup` - A callback for when the window is created and is about to start loading the frontend assets +- `OnShutdown` - A callback for when the application is about to quit +- `Bind` - A slice of struct instances that we wish to expose to the frontend + +A full list of application options can be found in the [Options Reference](/docs/reference/options). + +#### Assets + +The `Assets` option is mandatory as you can't have a Wails application without frontend assets. Those assets can be +any files you would expect to find in a web application - html, js, css, svg, png, etc. **There is no requirement to +generate asset bundles** - plain files will do. When the application starts, it will attempt to load `index.html` +from your assets and the frontend will essentially work as a browser from that point on. It is worth noting that +there is no requirement on where in the `embed.FS` the files live. It is likely that the embed path uses a nested +directory relative to your main application code, such as `frontend/dist`: + +```go title="main.go" +//go:embed frontend/dist +var assets embed.FS +``` + +At startup, Wails will iterate the embedded files looking for the directory containing `index.html`. All other assets will be loaded relative +to this directory. + +As production binaries use the files contained in `embed.FS`, there are no external files required to be shipped with +the application. + +When running in development mode using the `wails dev` command, the assets are loaded off disk, and any changes result +in a "live reload". The location of the assets needs to be passed to the `wails dev` command using the `-assetdir` flag +and is likely to be the same as the embed path. It is hoped that in the future we can calculate this from the `embed.FS` +itself. + +More details can be found in the [Application Development Guide](/docs/guides/application-development). + +#### Application Lifecycle Callbacks + +Just before the frontend is about to load `index.html`, a callback is made to the function provided in [OnStartup](/docs/reference/options#OnStartup). +A standard Go context is passed to this method. This context is required when calling the runtime so a standard pattern is to save +a reference to in this method. Just before the application shuts down, the [OnShutdown](/docs/reference/options#OnShutdown) callback is called in the same way, +again with the context. There is also an [OnDomReady](/docs/reference/options#OnDomReady) callback for when the frontend +has completed loading all assets in `index.html` and is equivalent of the [`body onload`](https://www.w3schools.com/jsref/event_onload.asp) event in Javascript. + +#### Method Binding + +The `Bind` option is one of the most important options in a Wails application. It specifies which struct methods +to expose to the frontend. When the application starts, it examines the struct instances listed in `Bind`, determines +which methods are public (starts with an uppercase letter) and will generate Javascript versions of those methods that +can be called by the frontend code. + +These methods are located in the frontend at `window.go...`. +In the example above, we bind `app`, which has one public method `Greet`. +This can be called in Javascript by calling `window.go.main.App.Greet`. +These methods return a promise. A successful call will result in the first return value from the Go call to be passed +to the resolve handler. An unsuccessful call is when a Go method that has an error type as it's second return value, +passes an error instance back to the caller. This is passed back via the reject handler. +In the example above, `Greet` only returns a `string` so the Javascript call will never reject - unless invalid data +is passed to it. + +All data types are correctly translated between Go and Javascript. Even structs. If you return a struct from a Go call, +it will be returned to your frontend as a Javascript map. Note: If you wish to use structs, you **must** define `json` struct +tags for your fields! It is also possible to send structs back to Go. Any Javascript map passed as an argument that +is expecting a struct, will be converted to that struct type. To make this process a lot easier, in `dev` mode, +a TypeScript module is generated, defining all the struct types used in bound methods. Using this module, it's possible +to construct and send native Javascript objects to the Go code. + +More information on Binding can be found in the [Binding Methods](/docs/guides/application-development#binding-methods) +section of the [Application Development Guide](/docs/guides/application-development). + +## The Frontend + +### Overview + +The frontend is a collection of files rendered by webkit. It's like a browser and webserver in one. +There is virtually[^1] no limit to which frameworks or libraries you can use. The main points of interaction between +the frontend and your Go code are: + +- Calling bound Go methods +- Calling runtime methods + +[^1]: + There is a very small subset of libraries that use features unsupported in WebViews. There are often alternatives and + workarounds for such cases. + +### Calling bound Go methods + +All bound Go methods are available at `window.go...`. As stated in +the previous section, these return a Promise where a successful call returns a value to the +resolve handler and an error returns a value to the reject handler. + +```go title="mycode.js" +window.go.main.App.Greet("Bill").then((result) => { + console.log("The greeting is: " + result); +}) +``` + +When running the application in `dev` mode, a javascript module is generated that wraps these +methods with JSDoc annotations. This really help with development, especially as most +IDEs will process JSDoc to provide code completion and type hinting. This module is called `go` +and is generated in the directory specified by the `wailsjsdir` flag. In this module is a file +called `bindings.js` containing these wrappers. For the above example, the file contains the +following code: + +```js title="bindings.js" +const go = { + main: { + App: { + /** + * Greet + * @param {Person} arg1 - Go Type: string + * @returns {Promise} - Go Type: string + */ + Greet: (arg1) => { + return window.go.main.App.Greet(arg1); + }, + }, + }, +}; +export default go; +``` + +#### Support for structs + +There is also additional support for Go methods that use structs in their signature. All Go structs +specified by bound method (either as parameters or return types) will have Typescript versions auto +generated as part of the Go code wrapper module. Using these, it's possible to share the same data +model between Go and Javascript. These models align with the JSDoc annotations, empowering IDE code +completion. + +Example: We update our `Greet` method to accept a `Person` instead of a string: + +```go title="main.go" +type Person struct { + Name string `json:"name"` + Age uint8 `json:"age"` + Address *Address `json:"address"` +} + +type Address struct { + Street string `json:"street"` + Postcode string `json:"postcode"` +} + +func (a *App) Greet(p Person) string { + return fmt.Sprintf("Hello %s (Age: %d)!", p.Name, p.Age) +} +``` + +Our `bindings.js` file has now been updated to reflect the change: + +```js title="bindings.js" +const go = { + main: { + App: { + /** + * Greet + * @param {Person} arg1 - Go Type: main.Person + * @returns {Promise} - Go Type: string + */ + Greet: (arg1) => { + return window.go.main.App.Greet(arg1); + }, + }, + }, +}; +export default go; +``` + +Alongside `bindings.js`, there is a file called `models.ts`. This contains our Go structs in TypeScript form: + +```ts title="models.ts" +export class Address { + street: string; + postcode: string; + + static createFrom(source: any = {}) { + return new Address(source); + } + + constructor(source: any = {}) { + if ("string" === typeof source) source = JSON.parse(source); + this.street = source["street"]; + this.postcode = source["postcode"]; + } +} +export class Person { + name: string; + age: number; + address?: Address; + + static createFrom(source: any = {}) { + return new Person(source); + } + + constructor(source: any = {}) { + if ("string" === typeof source) source = JSON.parse(source); + this.name = source["name"]; + this.age = source["age"]; + this.address = this.convertValues(source["address"], Address); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map((elem) => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } +} +``` + +So long as you have TypeScript as part of your frontend build configuration, you can use these models in +the following way: + +```js title="mycode.js" +import go from "./wailsjs/go/bindings"; +import { Person } from "./wailsjs/go/models"; + +let name = ""; + +function greet(name) { + let p = new Person(); + p.name = name; + p.age = 42; + go.main.App.Greet(p).then((result) => { + console.log(result); + }); +} +``` + +The combination of JSDoc and TypeScript generated models makes for a powerful development environment. + +### Calling runtime methods + +The Javascript runtime is located at `window.runtime` and contains many methods to do various +tasks such as emit an event or perform logging operations: + +```js title="mycode.js" +window.runtime.EventsEmit("my-event", 1); +``` + +More details about the JS runtime can be found in the [Runtime Reference](/docs/reference/runtime/intro). diff --git a/website/docs/reference/_category_.json b/website/docs/reference/_category_.json new file mode 100644 index 000000000..ebb337b83 --- /dev/null +++ b/website/docs/reference/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Reference", + "position": 40 +} diff --git a/website/docs/reference/cli.mdx b/website/docs/reference/cli.mdx new file mode 100644 index 000000000..644d8dec6 --- /dev/null +++ b/website/docs/reference/cli.mdx @@ -0,0 +1,180 @@ +--- +sidebar_position: 2 +--- + +# CLI + +The Wails CLI has a number of commands that are used for managing your projects. All commands are run in the following way: + +`wails ` + +## init + +`wails init` is used for generating projects. + +| Flag | Description | Default | +| :------------------- | :------------------------------------- | :------------------------- | +| -n "project name" | Name of the project. **Mandatory**. | | +| -d "project dir" | Project directory to create | Name of the project | +| -g | Initialise git repository | | +| -l | List available project templates | | +| -q | Suppress output to console | | +| -t "template name" | The project template to use. This can be the name of a default template or a URL to a remote template hosted on github. | vanilla | +| -ide | Generate IDE project files | | +| -f | Force build application | false | + +Example: + `wails init -n test -d mytestproject -g -ide vscode -q` + +This will generate a a project called "test" in the "mytestproject" directory, initialise git, +generate vscode project files and do so silently. + +More information on using IDEs with Wails can be found [here](/docs/guides/ides). + +### Remote Templates + +Remote templates (hosted on GitHub) are supported and can be installed by using the template's project URL. + +Example: + `wails init -n test -t https://github.com/leaanthony/testtemplate` + +A list of community maintained templates can be found [here](/docs/community/templates) + +:::warning Attention + + **The Wails project does not maintain, is not responsible nor liable for 3rd party templates!** + + If you are unsure about a template, inspect `package.json` and `wails.json` for what scripts are run and what packages are installed. + +::: + +## build + +`wails build` is used for compiling your project to a production-ready binary. + +| Flag | Description | Default | +| :------------------- | :-------------------------------------- | :------------------------- | +| -clean | Cleans the `build/bin` directory | | +| -compiler "compiler"| Use a different go compiler to build, eg go1.15beta1 | go | +| -ldflags "flags" | Additional ldflags to pass to the compiler | | +| -nopackage | Do not package application | | +| -o filename | Output filename | | +| -s | Skip building the frontend | | +| -f | Force build application | false | +| -tags "extra tags" | Build tags to pass to compiler (quoted and space separated) | | +| -upx | Compress final binary using "upx" | | +| -upxflags | Flags to pass to upx | | +| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 | +| -webview2 | WebView2 installer strategy: download,embed,browser,error | download | + +If you prefer to build using standard Go tooling, please consult the [Manual Builds](/docs/guides/manual-builds) +guide. + +Example: + +`wails build -clean -o myproject.exe` + +## doctor + +`wails doctor` will run diagnostics to ensure that your system is ready for development. + +Example: +``` +Wails CLI v2.0.0-beta + +Scanning system - Please wait (this may take a long time)...Done. + +System +------ +OS: Windows 10 Pro +Version: 2009 (Build: 19043) +ID: 21H1 +Go Version: go1.17 +Platform: windows +Architecture: amd64 + +Dependency Package Name Status Version +---------- ------------ ------ ------- +WebView2 N/A Installed 93.0.961.52 +npm N/A Installed 6.14.15 +*upx N/A Installed upx 3.96 + +* - Optional Dependency + +Diagnosis +--------- +Your system is ready for Wails development! + +``` + +## dev + +`wails dev` is used to run your application in a "live development" mode. This means: + + - The application is compiled and run automatically + - A watcher is started and will trigger a rebuild of your dev app if it detects changes to your go files + - A webserver is started on `http://localhost:34115` which serves your application (not just frontend) over http. This allows you to use your favourite browser development extensions + - All application assets are loaded from disk. If they are changed, the application will automatically reload (not rebuild). All connected browsers will also reload + - A JS module is generated that provides the following: + - Javascript wrappers of your Go methods with autogenerated JSDoc, providing code hinting + - TypeScript versions of your Go structs, that can be constructed and passed to your go methods + - A second JS module is generated that provides a wrapper + TS declaration for the runtime + +| Flag | Description | Default | +| :------------------- | :-------------------------------------- | :------------------------- | +| -assetdir "./path/to/assets" | The path to your compiled assets | Value in `wails.json` | +| -browser | Opens a browser to `http://localhost:34115` on startup | | +| -compiler "compiler"| Use a different go compiler to build, eg go1.15beta1 | go | +| -e | Extensions to trigger rebuilds (comma separated) | go | +| -ldflags "flags" | Additional ldflags to pass to the compiler | | +| -tags "extra tags" | Build tags to pass to compiler (quoted and space separated) | | +| -loglevel "loglevel"| Loglevel to use - Trace, Debug, Info, Warning, Error | Debug | +| -noreload | Disable automatic reload when assets change | | +| -v | Verbosity level (0 - silent, 1 - standard, 2 - verbose) | 1 | +| -wailsjsdir | The directory to generate the generated Wails JS modules | Value in `wails.json` | +| -debounce | The time to wait for reload after an asset change is detected | 100 (milliseconds) | +| -devserverurl "url" | Use 3rd party dev server url, EG Vite | "http://localhost:34115" | + +If the `assetdir`, `wailsjsdir`, `debounce` or `devserverurl` flags are provided on the command line, they are saved in +`wails.json`, and become the defaults for subsequent invocations. + +Example: + +`wails dev -assetdir ./frontend/dist -wailsjsdir ./frontend/src -browser` + +This command will do the following: + + - Build the application and run it (more details [here](/docs/guides/manual-builds) + - Generate the Wails JS modules in `./frontend/src` + - Watch for updates to files in `./frontend/dist` and reload on any change + - Open a browser and connect to the application + +There is more information on using this feature with existing framework scripts [here](/docs/guides/application-development#live-reloading). + +## generate + +### template + +Wails uses templates for project generation. The `wails generate template` command helps scaffold a template so that +it may be used for generating projects. + +| Flag | Description | +| :------------------- | :------------------------------------------- | +| -name | The template name (Mandatory) | +| -frontend "path" | Path to frontend project to use in template | + +For more details on creating templates, consult the [Templates guide](/docs/guides/templates). + +## update + +`wails update` will update the version of the Wails CLI. + +| Flag | Description | +| :------------------- | :-------------------------------------- | +| -pre | Update to latest pre-release version | +| -version "version" | Install a specific version of the CLI | + + +## version + +`wails version` will simply output the current CLI version. \ No newline at end of file diff --git a/website/docs/reference/menus.mdx b/website/docs/reference/menus.mdx new file mode 100644 index 000000000..96949456e --- /dev/null +++ b/website/docs/reference/menus.mdx @@ -0,0 +1,233 @@ +--- +sidebar_position: 4 +--- + +# Menus + +It is possible to add an application menu to Wails projects. This is achieved by defining a [Menu](#menu) struct and +calling the runtime method [MenuSetApplicationMenu](/docs/reference/runtime/menu#menusetapplicationmenu). + +It is also possible to dynamically update the menu, by updating the menu struct and calling +[MenuUpdateApplicationMenu](/docs/reference/runtime/menu#menuupdateapplicationmenu). + +Example: + +```go + myMenu := menu.NewMenuFromItems( + menu.SubMenu("File", menu.NewMenuFromItems( + menu.Text("&Open", keys.CmdOrCtrl("o"), openFile), + menu.Separator(), + menu.Text("Quit", keys.CmdOrCtrl("q"), func(_ *menu.CallbackData) { + runtime.Quit() + }), + )), + ) + + runtime.MenuSetApplicationMenu(myMenu) + +``` + +The example above uses helper methods, however it's possible to build the menu structs manually. + +## Menu + +A Menu is a collection of MenuItems: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu" +type Menu struct { + Items []*MenuItem +} +``` + +For the Application menu, each MenuItem represents a single menu such as "Edit". + +A simple helper method is provided for building menus: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu" +func NewMenuFromItems(first *MenuItem, rest ...*MenuItem) *Menu +``` + +This makes the layout of the code more like that of a menu without the need to add the menu items manually after creating them. +Alternatively, you can just create the menu items and add them to the menu manually. + +## MenuItem + +A MenuItem represents an item within a Menu. + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu" +// MenuItem represents a menu item contained in a menu +type MenuItem struct { + Label string + Role Role + Accelerator *keys.Accelerator + Type Type + Disabled bool + Hidden bool + Checked bool + SubMenu *Menu + Click Callback +} +``` + +| Field | Type | Notes | +| ---------------- | ---------------------------------- | ----------------------------------------------------- | +| Label | string | The menu text | +| Accelerator | [\*keys.Accelerator](#accelerator) | Key binding for this menu item | +| Type | [Type](#type) | Type of MenuItem | +| Disabled | bool | Disables the menu item | +| Hidden | bool | Hides this menu item | +| Checked | bool | Adds check to item (Checkbox & Radio types) | +| SubMenu | [\*Menu](#menu) | Sets the submenu | +| Click | [Callback](#callback) | Callback function when menu clicked | + +### Accelerator + +Accelerators (sometimes called keyboard shortcuts) define a binding between a keystroke and a menu item. Wails defines +an Accelerator as a combination or key + [Modifier](#modifier). They are available in the `"github.com/wailsapp/wails/v2/pkg/menu/keys"` package. + +Example: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu/keys" + // Defines cmd+o on Mac and ctrl-o on Window/Linux + myShortcut := keys.CmdOrCtrl("o") +``` + +Keys are any single character on a keyboard with the exception of `+`, which is defined as `plus`. +Some keys cannot be represented as characters so there are a set of named characters that may be used: + +- `backspace` +- `tab` +- `return` +- `enter` +- `escape` +- `left` +- `right` +- `up` +- `down` +- `space` +- `delete` +- `home` +- `end` +- `page up` +- `page down` +- `f1` +- `f2` +- `f3` +- `f4` +- `f5` +- `f6` +- `f7` +- `f8` +- `f9` +- `f10` +- `f11` +- `f12` +- `f13` +- `f14` +- `f15` +- `f16` +- `f17` +- `f18` +- `f19` +- `f20` +- `f21` +- `f22` +- `f23` +- `f24` +- `f25` +- `f26` +- `f27` +- `f28` +- `f29` +- `f30` +- `f31` +- `f32` +- `f33` +- `f34` +- `f35` +- `numlock` + +Wails also supports parsing accelerators using the same syntax as Electron. This is useful for storing accelerators in +config files. + +Example: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu/keys" + // Defines cmd+o on Mac and ctrl-o on Window/Linux + myShortcut, err := keys.Parse("Ctrl+Option+A") +``` + +#### Modifier + +The following modifiers are keys that may be used in combination with the accelerator key: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu/keys" +const ( + // CmdOrCtrlKey represents Command on Mac and Control on other platforms + CmdOrCtrlKey Modifier = "cmdorctrl" + // OptionOrAltKey represents Option on Mac and Alt on other platforms + OptionOrAltKey Modifier = "optionoralt" + // ShiftKey represents the shift key on all systems + ShiftKey Modifier = "shift" + // ControlKey represents the control key on all systems + ControlKey Modifier = "ctrl" +) +``` +A number of helper methods are available to create Accelerators using modifiers: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu/keys" +func CmdOrCtrl(key string) *Accelerator +func OptionOrAlt(key string) *Accelerator +func Shift(key string) *Accelerator +func Control(key string) *Accelerator +``` + +Modifiers can be combined using `keys.Combo(key string, modifier1 Modifier, modifier2 Modifier, rest ...Modifier)`: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu/keys" + // Defines "Ctrl+Option+A" on Mac and "Ctrl+Alt+A" on Window/Linux + myShortcut := keys.Combo("a", ControlKey, OptionOrAltKey) +``` + +### Type + +Each menu item must have a type and there are 5 types available: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu" +const ( + TextType Type = "Text" + SeparatorType Type = "Separator" + SubmenuType Type = "Submenu" + CheckboxType Type = "Checkbox" + RadioType Type = "Radio" +) +``` + +For convenience, helper methods are provided to quickly create a menu item: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu" +func Text(label string, accelerator *keys.Accelerator, click Callback) *MenuItem +func Separator() *MenuItem +func Radio(label string, selected bool, accelerator *keys.Accelerator, click Callback) *MenuItem +func Checkbox(label string, checked bool, accelerator *keys.Accelerator, click Callback) *MenuItem +func SubMenu(label string, menu *Menu) *MenuItem +``` + +A note on radio groups: A radio group is defined as a number of radio menu items that are next to each other in the menu. +This means that you do not need to group items together as it is automatic. However, that also means you cannot have 2 +radio groups next to each other - there must be a non-radio item between them. + +### Callback + +Each menu item may have a callback that is executed when the item is clicked: + +```go title="Package: github.com/wailsapp/wails/v2/pkg/menu" +type Callback func(*CallbackData) + +type CallbackData struct { + MenuItem *MenuItem +} +``` + +The function is given a `CallbackData` struct which indicates which menu item triggered the callback. This is useful when +using radio groups that may share a callback. diff --git a/website/docs/reference/options.mdx b/website/docs/reference/options.mdx new file mode 100644 index 000000000..bc1e8df6e --- /dev/null +++ b/website/docs/reference/options.mdx @@ -0,0 +1,283 @@ +--- +sidebar_position: 3 +--- + +# Options + +## Application Options + +The `Options.App` struct contains the application configuration. +It is passed to the `wails.Run()` method: + +```go title="Example" +import "github.com/wailsapp/wails/v2/pkg/options" + +func main() { + + err := wails.Run(&options.App{ + Title: "Menus Demo", + Width: 800, + Height: 600, + DisableResize: false, + Fullscreen: false, + Frameless: true, + MinWidth: 400, + MinHeight: 400, + MaxWidth: 1280, + MaxHeight: 1024, + StartHidden: false, + HideWindowOnClose: false, + RGBA: &options.RGBA{R: 0, G: 0, B: 0, A: 255}, + Assets: assets, + Menu: app.applicationMenu(), + Logger: nil, + LogLevel: logger.DEBUG, + OnStartup: app.startup, + OnDomReady: app.domready, + OnShutdown: app.shutdown, + Bind: []interface{}{ + app, + }, + Windows: &windows.Options{ + WebviewIsTransparent: false, + WindowIsTranslucent: false, + DisableWindowIcon: false, + }, + }) + if err != nil { + log.Fatal(err) + } +} + +``` + + + +### Title + +Name: Title + +Type: string + +The text shown in the window's title bar. + +### Width + +Name: Width + +Type: int + +The initial width of the window. +Default: 1024. + +### Height + +Name: Height + +Type: int + +The initial height of the window. +Default: 768 + +### DisableResize + +Name: DisableResize + +Type: bool + +By default, the main window is resizable. Setting this to `true` will keep it a fixed size. + +### Fullscreen + +Name: Fullscreen + +Type: bool + +Setting this to `true` will make the window fullscreen at startup. + +### Frameless + +Name: Frameless + +Type: bool + +When set to `true`, the window will have no borders or title bar. +Also see [Frameless Windows](/docs/guides/frameless). + +### MinWidth + +Name: MinWidth + +Type: int + +This sets the minimum width for the window. If the value given in `Width` is less than this value, +the window will be set to `MinWidth` by default. + +### MinHeight + +Name: MinHeight + +Type: int + +This sets the minimum height for the window. If the value given in `Height` is less than this value, +the window will be set to `MinHeight` by default. + +### MaxWidth + +Name: MaxWidth + +Type: int + +This sets the maximum width for the window. If the value given in `Width` is more than this value, +the window will be set to `MaxWidth` by default. + +### MaxHeight + +Name: MaxHeight + +Type: int + +This sets the maximum height for the window. If the value given in `Height` is more than this value, +the window will be set to `MaxHeight` by default. + +### StartHidden + +Name: StartHidden + +Type: bool + +When set to `true`, the application will be hidden until [WindowShow](/docs/reference/runtime/window#WindowShow) +is called. + +### HideWindowOnClose + +Name: HideWindowOnClose + +Type: bool + +By default, closing the window will close the application. Setting this to `true` means closing the window will +hide the window instead. + +### RGBA + +Name: RGBA + +Type: int (0xRRGGBBAA) +Example: 0xFF000088 - Red at 50% transparency + +This value is the RGBA value to set the window by default. +Default: 0xFFFFFFFF. + +### AlwaysOnTop + +Name: AlwaysOnTop + +Type: bool + +Indicates that the window should stay above other windows when losing focus. + +### Assets + +Name: Assets + +Type: \*embed.FS + +The frontend assets to be used by the application. Requires an `index.html` file. + +### Menu + +Name: Menu + +Type: \*menu.Menu + +The menu to be used by the application. More details about Menus in the [Menu Reference](/docs/reference/runtime/menu). + +### Logger + +Name: Logger + +Type: logger.Logger +Default: Logger to Stdout + +The logger to be used by the application. More details about logging in the [Log Reference](/docs/reference/runtime/log). + +### LogLevel + +Name: LogLevel + +Type: logger.LogLevel +Default: `Info` in dev mode, `Error` in production mode + +The default log level. More details about logging in the [Log Reference](/docs/reference/runtime/log). + +### OnStartup + +Name: OnStartup + +Type: func(ctx context.Context) + +This callback is called after the frontend has been created, but before `index.html` has been loaded. It is given +the application context. + +### OnDomReady + +Name: OnDomReady + +Type: func(ctx context.Context) + +This callback is called after the frontend has loaded `index.html` and the DOM is ready. It is given +the application context. + +### OnShutdown + +Name: OnShutdown + +Type: func(ctx context.Context) + +This callback is called after the frontend has been destroyed, just before the application terminates. It is given +the application context. + +### Bind + +Name: Bind + +Type: []interface{} + +A slice of struct instances defining methods that need to be bound to the frontend. + +### Windows + +Name: Windows + +Type: \*windows.Options + +This defines [Windows specific options](#windows-specific-options). + +## Windows Specific Options + +### WebviewIsTransparent + +Name: WebviewIsTransparent + +Type: bool + +Setting this to `true` will make the webview background transparent when an alpha value of `0` is used. +This means that if you use `rgba(0,0,0,0)`, the host window will show through. +Often combined with [WindowIsTranslucent](#WindowIsTranslucent) to make frosty-looking applications. + +### WindowIsTranslucent + +Name: WindowIsTranslucent + +Type: bool + +Setting this to `true` will make the window background translucent. Often combined +with [WebviewIsTransparent](#WebviewIsTransparent) to make frosty-looking applications. + +### DisableWindowIcon + +Name: DisableWindowIcon + +Type: bool + +Setting this to true will remove the icon in the top left corner of the title bar. diff --git a/website/docs/reference/project-config.mdx b/website/docs/reference/project-config.mdx new file mode 100644 index 000000000..ccf9cd9b2 --- /dev/null +++ b/website/docs/reference/project-config.mdx @@ -0,0 +1,27 @@ +--- +sidebar_position: 5 +--- + +# Project Config + +The project config resides in the `wails.json` file in the project directory. The structure of the config is: + +```json +{ + "name": "[The project name]", + "assetdir": "[Relative path to your assets directory]", + "frontend:install": "[The command to install node dependencies, run in the frontend directory - often `npm install`]", + "frontend:build": "[The command to build the assets, run in the frontend directory - often `npm run build`]", + "frontend:dev": "[This command is run in a separate process on `wails dev`. Useful for 3rd party watchers]", + "wailsjsdir": "[Relative path to the directory that the auto-generated JS modules will be created]", + "version": "[Project config version]", + "outputfilename": "[The name of the binary]", + "debounceMS": 100, // The default time the dev server waits to reload when it detects a vhange in assets + "devserverurl": "[URL to the dev server serving local assets. Default: http://localhost:34115]" +} +``` + +This file is read by the Wails CLI when running `wails build` or `wails dev`. + +The `assetdir`, `wailsjsdir`, `debounceMS` and `devserverurl` flags in `wails build/dev` will update the project config +and thus become defaults for subsequent runs. \ No newline at end of file diff --git a/website/docs/reference/runtime/_category_.json b/website/docs/reference/runtime/_category_.json new file mode 100644 index 000000000..ac6d55488 --- /dev/null +++ b/website/docs/reference/runtime/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Runtime", + "position": 1 +} diff --git a/website/docs/reference/runtime/browser.mdx b/website/docs/reference/runtime/browser.mdx new file mode 100644 index 000000000..1cb407d49 --- /dev/null +++ b/website/docs/reference/runtime/browser.mdx @@ -0,0 +1,20 @@ +--- +sidebar_position: 7 +--- + +# Browser + +## Overview + +These methods are related to the system browser. + +### BrowserOpenURL +Go Signature: `BrowserOpenURL(ctx context.Context, url string)` + +JS Signature: `BrowserOpenURL(url string)` + +Opens the given URL in the system browser. + + + + diff --git a/website/docs/reference/runtime/dialog.mdx b/website/docs/reference/runtime/dialog.mdx new file mode 100644 index 000000000..7e7717858 --- /dev/null +++ b/website/docs/reference/runtime/dialog.mdx @@ -0,0 +1,249 @@ +--- +sidebar_position: 5 +--- + +# Dialog + +## Overview + +This part of the runtime provides access to native dialogs, such as File Selectors and Message boxes. + +:::info Javascript + Dialog is currently unsupported in the JS runtime. +::: + +### OpenDirectoryDialog + +Opens a dialog that prompts the user to select a directory. Can be customised using [OpenDialogOptions](#opendialogoptions). + +Go Signature: `OpenDirectoryDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)` + +Returns: Selected directory (blank if the user cancelled) or an error + + +### OpenFileDialog + +Opens a dialog that prompts the user to select a file. Can be customised using [OpenDialogOptions](#opendialogoptions). + +Go Signature: `OpenFileDialog(ctx context.Context, dialogOptions OpenDialogOptions) (string, error)` + +Returns: Selected file (blank if the user cancelled) or an error + + + +### OpenMultipleFilesDialog + +Opens a dialog that prompts the user to select multiple files. Can be customised using [OpenDialogOptions](#opendialogoptions). + +Go Signature: `OpenMultipleFilesDialog(ctx context.Context, dialogOptions OpenDialogOptions) ([]string, error)` + +Returns: Selected files (nil if the user cancelled) or an error + + + +### SaveFileDialog + +Opens a dialog that prompts the user to select a filename for the purposes of saving. Can be customised using [SaveDialogOptions](#savedialogoptions). + +Go Signature: `SaveFileDialog(ctx context.Context, dialogOptions SaveDialogOptions) (string, error)` + +Returns: The selected file (blank if the user cancelled) or an error + + + +### MessageDialog + +Displays a message using a message dialog. Can be customised using [MessageDialogOptions](#messagedialogoptions). + +Go Signature: `MessageDialog(ctx context.Context, dialogOptions MessageDialogOptions) (string, error)` + +Returns: The text of the selected button or an error + +## Options + +### OpenDialogOptions + +```go +type OpenDialogOptions struct { + DefaultDirectory string + DefaultFilename string + Title string + Filters []FileFilter + AllowFiles bool + AllowDirectories bool + ShowHiddenFiles bool + CanCreateDirectories bool + ResolvesAliases bool + TreatPackagesAsDirectories bool +} +``` +| Field | Description | Win | Mac | +| -------------------------- | ---------------------------------------------- | --- | --- | +| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | +| DefaultFilename | The default filename | ✅ | ✅ | +| Title | Title for the dialog | ✅ | ✅ | +| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | +| AllowFiles | Allow files to be selected | | ✅ | +| AllowDirectories | Allow directories to be selected | | ✅ | +| ShowHiddenFiles | Show files hidden by the system | | ✅ | +| CanCreateDirectories | Allow user to create directories | | ✅ | +| ResolvesAliases | If true, returns the file not the alias | | ✅ | +| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | + + +### SaveDialogOptions + +```go +type SaveDialogOptions struct { + DefaultDirectory string + DefaultFilename string + Title string + Filters []FileFilter + ShowHiddenFiles bool + CanCreateDirectories bool + TreatPackagesAsDirectories bool +} +``` + +| Field | Description | Win | Mac | +| -------------------------- | ---------------------------------------------- | --- | --- | +| DefaultDirectory | The directory the dialog will show when opened | ✅ | ✅ | +| DefaultFilename | The default filename | ✅ | ✅ | +| Title | Title for the dialog | ✅ | ✅ | +| [Filters](#filefilter) | A list of file filters | ✅ | ✅ | +| ShowHiddenFiles | Show files hidden by the system | | ✅ | +| CanCreateDirectories | Allow user to create directories | | ✅ | +| TreatPackagesAsDirectories | Allow navigating into packages | | ✅ | + +### MessageDialogOptions + +```go +type MessageDialogOptions struct { + Type DialogType + Title string + Message string + Buttons []string + DefaultButton string + CancelButton string +} +``` +| Field | Description | Win | Mac | +| ------------- | ------------------------------------------------------------------------- | --- | --- | +| Type | The type of message dialog, eg question, info... | ✅ | ✅ | +| Title | Title for the dialog | ✅ | ✅ | +| Message | The message to show the user | ✅ | ✅ | +| Buttons | A list of button titles | | ✅ | +| DefaultButton | The button with this text should be treated as default. Bound to `return` | | ✅ | +| CancelButton | The button with this text should be treated as cancel. Bound to `escape` | | ✅ | + +#### Windows + +Windows has standard dialog types in which the buttons are not customisable. +The value returned will be one of: "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "Try Again" or "Continue" + +#### Mac + +A message dialog on Mac may specify up to 4 buttons. If no `DefaultButton` or `CancelButton` is given, the first button +is considered default and is bound to the `return` key. + +For the following code: +```go +selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{ + Title: "It's your turn!", + Message: "Select a number", + Buttons: []string{"one", "two", "three", "four"}, +}) +``` +the first button is shown as default: +
+ +
+
+ +And if we specify `DefaultButton` to be "two": +```go +selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{ + Title: "It's your turn!", + Message: "Select a number", + Buttons: []string{"one", "two", "three", "four"}, + DefaultButton: "two", +}) +``` +the second button is shown as default. When `return` is pressed, the value "two" is returned. +
+ +
+
+ +If we now specify `CancelButton` to be "three": +```go +selection, err := runtime.MessageDialog(b.ctx, runtime.MessageDialogOptions{ + Title: "It's your turn!", + Message: "Select a number", + Buttons: []string{"one", "two", "three", "four"}, + DefaultButton: "two", + CancelButton: "three", +}) +``` +the button with "three" is shown at the bottom of the dialog. When `escape` is pressed, the value "three" is returned: +
+ +
+
+
+
+ + +#### DialogType + +```go +const ( + InfoDialog DialogType = "info" + WarningDialog DialogType = "warning" + ErrorDialog DialogType = "error" + QuestionDialog DialogType = "question" + ) +``` + +### FileFilter + +```go +type FileFilter struct { + DisplayName string // Filter information EG: "Image Files (*.jpg, *.png)" + Pattern string // semi-colon separated list of extensions, EG: "*.jpg;*.png" +} +``` + +#### Windows + +Windows allows you to use multiple file filters in dialog boxes. Each FileFilter will show up as a separate entry in the +dialog: + +
+ +
+
+
+
+ +#### Mac + +Mac dialogs only have the concept of a single set of patterns to filter files. If multiple FileFilters are provided, +Wails will use all the Patterns defined. + +Example: +```go + selection, err := runtime.OpenFileDialog(b.ctx, runtime.OpenDialogOptions{ + Title: "Select File", + Filters: []runtime.FileFilter{ + { + DisplayName: "Images (*.png;*.jpg)", + Pattern: "*.png;*.jpg", + }, { + DisplayName: "Videos (*.mov;*.mp4)", + Pattern: "*.mov;*.mp4", + }, + }, + }) +``` +This will result in the Open File dialog using `*.png,*.jpg,*.mov,*.mp4` as a filter. \ No newline at end of file diff --git a/website/docs/reference/runtime/events.mdx b/website/docs/reference/runtime/events.mdx new file mode 100644 index 000000000..c08f83c8a --- /dev/null +++ b/website/docs/reference/runtime/events.mdx @@ -0,0 +1,51 @@ +--- +sidebar_position: 2 +--- + +# Events + +## Overview + +The Wails runtime provides a unified events system, where events can be emitted or received by either Go or Javascript. +Optionally, data may be passed with the events. Listeners will receive the data in the local data types. + +### EventsOn + +Go Signature: `EventsOn(ctx context.Context, eventName string, callback func(optionalData ...interface{}))` + +JS Signature: `EventsOn(eventName string, callback function(optionalData?: any))` + +This method sets up a listener for the given event name. When an event of type `eventName` is [emitted](#EventsEmit), +the callback is triggered. Any additional data sent with the emitted event will be passed to the callback. + +### EventsOff + +Go Signature: `EventsOff(ctx context.Context, eventName string)` + +JS Signature: `EventsOff(eventName string)` + +This method unregisters the listener for the given event name. + +### EventsOnce + +Go Signature: `EventsOnce(ctx context.Context, eventName string, callback func(optionalData ...interface{}))` + +JS Signature: `EventsOnce(eventName string, callback function(optionalData?: any))` + +This method sets up a listener for the given event name, but will only trigger once. + +### EventsOnMultiple + +Go Signature: `EventsOnMultiple(ctx context.Context, eventName string, callback func(optionalData ...interface{}), counter int)` + +JS Signature: `EventsOnMultiple(eventName string, callback function(optionalData?: any), counter int)` + +This method sets up a listener for the given event name, but will only trigger a maximum of `counter` times. + +### EventsEmit + +Go Signature: `EventsEmit(ctx context.Context, eventName string, optionalData ...interface{})` + +JS Signature: `EventsEmit(ctx context, optionalData function(optionalData?: any))` + +This method emits the given event. Optional data may be passed with the event. This will trigger any event listeners. diff --git a/website/docs/reference/runtime/intro.mdx b/website/docs/reference/runtime/intro.mdx new file mode 100644 index 000000000..421d7ef90 --- /dev/null +++ b/website/docs/reference/runtime/intro.mdx @@ -0,0 +1,16 @@ +--- +sidebar_position: 1 +--- + +# Introduction + +The runtime is a library that provides utility methods for your application. There is both a Go and Javascript runtime +and the aim is to try and keep them at parity where possible. + +The Go Runtime is available through importing `github.com/wailsapp/wails/v2/pkg/runtime`. All methods in this package +take a context as the first parameter. This context can be obtained from the [OnStartup](/docs/reference/options#OnStartup) +or [OnDomReady](/docs/reference/options#OnDomReady) hooks. + +The Javascript library is available to the frontend via the `window.runtime` map. There is a runtime package generated when using `dev` +mode that provides Typescript declarations for the runtime. This should be located in the `wailsjs` directory in your +frontend directory. diff --git a/website/docs/reference/runtime/log.mdx b/website/docs/reference/runtime/log.mdx new file mode 100644 index 000000000..3e0c4f36d --- /dev/null +++ b/website/docs/reference/runtime/log.mdx @@ -0,0 +1,114 @@ +--- +sidebar_position: 3 +--- + +# Log + +## Overview + +The Wails runtime provides a logging mechanism that may be called from Go or Javascript. Like most +loggers, there are a number of log levels: + + - Trace + - Debug + - Info + - Warning + - Error + - Fatal + +The logger will output any log message at the current, or higher, log level. Example: The `Debug` log +level will output all messages except `Trace` messages. + +### LogPrint + +Go Signature: `LogPrint(ctx context.Context, message string)` + +JS Signature: `LogPrint(message: string)` + +Logs the given message as a raw message. + +### LogTrace + +Go Signature: `LogTrace(ctx context.Context, message string)` + +JS Signature: `LogTrace(message: string)` + +Logs the given message at the `Trace` log level. + +### LogDebug + +Go Signature: `LogDebug(ctx context.Context, message string)` + +JS Signature: `LogDebug(message: string)` + +Logs the given message at the `Debug` log level. + + +### LogInfo + +Go Signature: `LogInfo(ctx context.Context, message string)` + +JS Signature: `LogInfo(message: string)` + +Logs the given message at the `Info` log level. + + +### LogWarning + +Go Signature: `LogWarning(ctx context.Context, message string)` + +JS Signature: `LogWarning(message: string)` + +Logs the given message at the `Warning` log level. + + +### LogError + +Go Signature: `LogError(ctx context.Context, message string)` + +JS Signature: `LogError(message: string)` + +Logs the given message at the `Error` log level. + + +### LogFatal +Go Signature: `LogFatal(ctx context.Context, message string)` + +JS Signature: `LogFatal(message: string)` + +Logs the given message at the `Fatal` log level. + +### LogSetLogLevel + +Go Signature: `LogSetLogLevel(ctx context.Context, level logger.LogLevel)` + +JS Signature: `LogSetLogLevel(level: number)` + +Sets the log level. In Javascript, the number relates to the following log levels: + +| Value | Log Level | +| ----- | --------- | +| 1 | Trace | +| 2 | Debug | +| 3 | Info | +| 4 | Warning | +| 5 | Error | + +## Using a Custom Logger + +A custom logger may be used by providing it using the [Logger](/docs/reference/options#logger) +application option. The only requirement is that the logger implements the `logger.Logger` interface +defined in `github.com/wailsapp/wails/v2/pkg/logger`: + +```go title="logger.go" +type Logger interface { + Print(message string) + Trace(message string) + Debug(message string) + Info(message string) + Warning(message string) + Error(message string) + Fatal(message string) +} +``` + diff --git a/website/docs/reference/runtime/menu.mdx b/website/docs/reference/runtime/menu.mdx new file mode 100644 index 000000000..3139224b8 --- /dev/null +++ b/website/docs/reference/runtime/menu.mdx @@ -0,0 +1,25 @@ +--- +sidebar_position: 6 +--- + +# Menu + +## Overview + +These methods are related to the application menu. + +:::info Javascript + Menu is currently unsupported in the JS runtime. +::: + +### MenuSetApplicationMenu +Go Signature: `MenuSetApplicationMenu(ctx context.Context, menu *menu.Menu)` + +Sets the application menu to the given [menu](/docs/reference/menus) . + +### MenuUpdateApplicationMenu +Go Signature: `MenuUpdateApplicationMenu(ctx context.Context)` + +Updates the application menu, picking up any changes to the menu passed to `MenuSetApplicationMenu`. + + diff --git a/website/docs/reference/runtime/window.mdx b/website/docs/reference/runtime/window.mdx new file mode 100644 index 000000000..91e33388f --- /dev/null +++ b/website/docs/reference/runtime/window.mdx @@ -0,0 +1,184 @@ +--- +sidebar_position: 4 +--- + +# Window + +## Overview + +These methods give control of the application window. + +### WindowSetTitle +Go Signature: `WindowSetTitle(ctx context.Context, title string)` + +JS Signature: `WindowSetTitle(title: string)` + +Sets the text in the window title bar. + +### WindowFullscreen +Go Signature: `WindowFullscreen(ctx context.Context)` + +JS Signature: `WindowFullscreen()` + +Makes the window full screen. + +### WindowUnFullscreen +Go Signature: `WindowUnFullscreen(ctx context.Context)` + +JS Signature: `WindowUnFullscreen()` + +Restores the previous window dimensions and position prior to full screen. + +### WindowCenter +Go Signature: `WindowCenter(ctx context.Context)` + +JS Signature: `WindowCenter()` + +Centers the window on the monitor the window is currently on. + +### WindowReload +Go Signature: `WindowReload(ctx context.Context)` + +JS Signature: `WindowReload()` + +Performs a "reload" (Reloads index.html) + +### WindowShow +Go Signature: `WindowShow(ctx context.Context)` + +JS Signature: `WindowShow()` + +Shows the window, if it is currently hidden. + +### WindowHide +Go Signature: `WindowHide(ctx context.Context)` + +JS Signature: `WindowHide()` + +Hides the window, if it is currently visible. + +### WindowSetSize +Go Signature: `WindowSetSize(ctx context.Context, width int, height int)` + +JS Signature: `WindowSetSize(size: Size)` + +Sets the width and height of the window. + +### WindowGetSize +Go Signature: `WindowGetSize(ctx context.Context) (width int, height int)` + +JS Signature: `WindowGetSize() : Size` + +Gets the width and height of the window. + +### WindowSetMinSize +Go Signature: `WindowSetMinSize(ctx context.Context, width int, height int)` + +JS Signature: `WindowSetMinSize(size: Size)` + +Sets the minimum window size. +Will resize the window if the window is currently smaller than the given dimensions. + +Setting a size of `0,0` will disable this constraint. + +### WindowSetMaxSize +Go Signature: `WindowSetMaxSize(ctx context.Context, width int, height int)` + +JS Signature: `WindowSetMaxSize(size: Size)` + +Sets the maximum window size. +Will resize the window if the window is currently larger than the given dimensions. + +Setting a size of `0,0` will disable this constraint. + +### WindowSetPosition +Go Signature: `WindowSetPosition(ctx context.Context, x int, y int)` + +JS Signature: `WindowSetPosition(position: Position)` + +Sets the window position relative to the monitor the window is currently on. + +### WindowGetPosition +Go Signature: `WindowGetPosition(ctx context.Context) (x int, y int)` + +JS Signature: `WindowGetPosition() : Position` + +Gets the window position relative to the monitor the window is currently on. + +### WindowMaximise +Go Signature: `WindowMaximise(ctx context.Context)` + +JS Signature: `WindowMaximise()` + +Maximises the window to fill the screen. + +### WindowUnmaximise +Go Signature: `WindowUnmaximise(ctx context.Context)` + +JS Signature: `WindowUnmaximise()` + +Restores the window to the dimensions and position prior to maximising. + +### WindowMinimise +Go Signature: `WindowMinimise(ctx context.Context)` + +JS Signature: `WindowMinimise()` + +Minimises the window. + +### WindowUnminimise +Go Signature: `WindowUnminimise(ctx context.Context)` + +JS Signature: `WindowUnminimise()` + +Restores the window to the dimensions and position prior to minimising. + +### WindowSetRGBA +Go Signature: `WindowSetRGBA(ctx context.Context, col *options.RGBA)` + +JS Signature: `WindowSetRGBA(col: RGBA)` + +Sets the background colour of the window to the given [RGBA](window#rgba) colour definition. +This colour will show through for all transparent pixels. + +Valid values for R, G, B and A are 0-255. + +:::info Windows + +On Windows, only alpha values of 0 or 255 are supported. +Any value that is not 0 will be considered 255. + +::: + +## Typescript Object Definitions + +### Position + +```ts +interface Position { + x: number; + y: number; +} +``` + +### Size + +```ts +interface Size { + w: number; + h: number; +} +``` + +### RGBA + +```ts +interface RGBA { + r: number; + g: number; + b: number; + a: number; +} +``` + + diff --git a/website/docs/stats.mdx b/website/docs/stats.mdx new file mode 100644 index 000000000..ef452fe9e --- /dev/null +++ b/website/docs/stats.mdx @@ -0,0 +1,24 @@ +--- +sidebar_position: 100 +--- + +# Website Stats + +To enable us to understand how better to focus our efforts on translations and platform support, +we use [Plausible](https://plausible.io/privacy-focused-web-analytics) to gather **anonymous** stats like country and platform. +We chose Plausible because we believe in respecting the privacy of our users. +We also believe in transparency, therefore have made the dashboard public. + +If you have any concerns or suggestions, please raise them in the projects github discussions. + + + + + +export const NewComponent = () => ( +
+