diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..9f3768a --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,43 @@ +name: Test +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + os: [linux] + tags: [[], ['null'], [portal], [uinput], [x11], + ['null', portal, uinput, x11]] + include: + - os: windows + tags: [] + - os: windows + tags: ['null'] + env: + GOOS: ${{ matrix.os }} + GOFLAGS: -tags=${{ join(matrix.tags, ',') }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: stable + check-latest: true + - run: sudo apt install libxrandr-dev libxtst-dev libxt-dev + if: ${{ contains(matrix.tags, 'x11') }} + + - name: Go Format + run: gofmt -s -w . && git diff --exit-code + + - name: Go Mod Tidy + run: go mod tidy && git diff --exit-code + + - name: Go Vet + run: go vet ./... + + - name: Go Build + run: go build ./... + + - name: Go Test (with data race detection) + run: go test -race ./... + if: ${{ matrix.os == 'linux' }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07c477a --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Binaries +remote-touchpad + +# Systemd service state +.remote-touchpad.service.state + +# Logs and run files +.rts-log + +# Environment files +.env \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index da04a01..e59454c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,119 @@ # Changelog +## 1.5.4 (2026-08-02) + +* Improve X11 keyboard input +* Update dependencies + +## 1.5.3 (2025-12-24) + +* Update dependencies + +## 1.5.2 (2025-10-12) + +* Update dependencies + +## 1.5.0 (2025-03-18) + +* Add restore token support for the remote desktop portal +* Update dependencies + +## 1.4.8 (2024-09-01) + +* Update dependencies + +## 1.4.7 (2024-08-30) + +* Update dependencies + +## 1.4.6 (2024-02-06) + +* Prevent incorrect MIME types + +## 1.4.5 (2024-01-31) + +* Keep text input open after sending +* Workaround for virtual keyboard covering buttons (Chromium & iOS) +* Enable hardware keyboard input in mouse mode + +## 1.4.4 (2024-01-23) + +* Fix Xwayland detection on newer servers +* Fix support for the KDE remote desktop portal +* Set correct minimum version for Go +* Update dependencies + +## 1.4.3 (2023-10-03) + +* Update dependencies + +## 1.4.2 (2023-05-20) + +* Fix Windows support + +## 1.4.1 (2023-05-11) + +* uinput: Fix vertical scroll direction +* Update dependencies + +## 1.4.0 (2023-04-25) + +* Add feedback to buttons +* Add numeric keypad +* Prevent touch triggering mouse mode + +## 1.3.0 (2023-03-03) + +* Initial support for uinput +* Update dependencies + +## 1.2.3 (2022-12-29) + +* Improve XWayland detection +* Update dependencies +* Flatpak: Add Wayland socket to disable X11 socket + +## 1.2.2 (2022-09-23) + +* Update dependencies +* Fix race in RemoteDesktop portal controller + +## 1.2.1 (2022-06-20) + +* Update dependencies + +## 1.2.0 (2022-02-26) + +* Add command-line options for move and scroll speed +* Add hardware mouse support +* Limit update rate +* Windows: Reverse vertical scroll direction +* Show error message if compiled without controller +* Don't show disabled controllers in "unsupported platform" error message +* Switch from go-bindata-assetfs to go:embed + +## 1.1.0 (2022-01-28) + +* Add more shortcut keys (Arrows, Delete, Enter, …) +* Add basic support for physical keyboard +* Allow fullscreen on keys page +* Enable touchpad on keys page +* Retain keyboard text when reloading website +* Improve compatibility with old browsers +* Write URL and QR code to stdout +* Ignore unparseable IP +* Remove trailing new line from QR code + +## 1.0.5 (2022-01-07) + +* Use relative URL for websocket +* Update dependencies + +## 1.0.4 (2021-11-06) + +* Disable browser touch gestures +* Update dependencies + ## 1.0.3 (2021-09-18) * Update dependencies diff --git a/README.md b/README.md index c6cc377..e039950 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,45 @@ Supports Flatpak's RemoteDesktop portal (for Wayland), Windows and X11. * [Snap](https://snapcraft.io/remote-touchpad) * [Windows](https://github.com/Unrud/remote-touchpad/releases/latest) * Golang: - * Portal & X11: `go get -u -tags portal,x11 github.com/Unrud/remote-touchpad` - * Windows: `get get -u github.com/Unrud/remote-touchpad` + * Portal & uinput & X11: + + ```sh + go install -tags portal,uinput,x11 github.com/unrud/remote-touchpad@latest + ``` + * Windows: + + ```sh + go install github.com/unrud/remote-touchpad@latest + ``` + +## Linux User Service Installation + +To install remote-touchpad as a user-specific systemd service (no root access required): + +1. Clone the repository and navigate to the project directory: + + ```sh + git clone https://github.com/Unrud/remote-touchpad.git + cd remote-touchpad + ``` + +2. Install the service using the provided wrapper script (no sudo required): + + ```sh + ./rts install + ``` + +3. Access the service: + + * View service status: `./rts status` + * Get URL: `./rts url` + * View logs: `./rts log` + * Stop service: `./rts stop` + * Start service: `./rts start` + * Restart service: `./rts restart` + * Uninstall: `./rts uninstall` + +The service will run in the background and maintain a persistent connection with URL and QR code accessible via the `url` command. ## Screenshots diff --git a/backend_portal.go b/backend_portal.go deleted file mode 100644 index d85cae1..0000000 --- a/backend_portal.go +++ /dev/null @@ -1,292 +0,0 @@ -// +build portal - -/* - * Copyright (c) 2018 Unrud - * - * This file is part of Remote-Touchpad. - * - * Remote-Touchpad is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Remote-Touchpad is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Remote-Touchpad. If not, see . - */ - -package main - -import ( - "errors" - "fmt" - "github.com/godbus/dbus/v5" - "sync" -) - -const ( - deviceKeyboard uint32 = 1 - devicePointer uint32 = 2 - - btnReleased uint32 = 0 - btnPressed uint32 = 1 - - // linux/input-event-codes.h - btnLeft int32 = 0x110 - btnRight int32 = 0x111 - btnMiddle int32 = 0x112 -) - -type portalBackend struct { - bus *dbus.Conn - remoteDesktop dbus.BusObject - sessionHandle dbus.ObjectPath - lock sync.RWMutex -} - -func InitPortalBackend() (Backend, error) { - bus, err := dbus.SessionBusPrivate() - if err != nil { - return nil, UnsupportedPlatformError{err} - } - cleanupBus := true - defer func() { - if cleanupBus { - bus.Close() - } - }() - err = bus.Auth(nil) - if err != nil { - return nil, UnsupportedPlatformError{err} - } - err = bus.Hello() - if err != nil { - return nil, UnsupportedPlatformError{err} - } - remoteDesktop := bus.Object("org.freedesktop.portal.Desktop", - "/org/freedesktop/portal/desktop") - availableDeviceTypesV, err := remoteDesktop.GetProperty( - "org.freedesktop.portal.RemoteDesktop.AvailableDeviceTypes") - if err != nil { - return nil, UnsupportedPlatformError{err} - } - availableDeviceTypes, ok := availableDeviceTypesV.Value().(uint32) - if !ok { - return nil, UnsupportedPlatformError{errors.New( - "unexpected 'AvailableDeviceTypes' return type")} - } - if availableDeviceTypes&deviceKeyboard == 0 || - availableDeviceTypes&devicePointer == 0 { - return nil, UnsupportedPlatformError{errors.New( - "keyboard or pointer source type not supported")} - } - inVardict := make(map[string]dbus.Variant) - inVardict["session_handle_token"] = dbus.MakeVariant("t") - result, outVardict, err := getResponse(bus, remoteDesktop, - "org.freedesktop.portal.RemoteDesktop.CreateSession", 0, inVardict) - if err != nil { - return nil, UnsupportedPlatformError{err} - } - if result != 0 { - return nil, UnsupportedPlatformError{errors.New(fmt.Sprintf( - "Calling 'CreateSession' failed (%v)", result))} - } - sessionHandleV, ok := outVardict["session_handle"] - if !ok { - return nil, UnsupportedPlatformError{errors.New( - "'session_handle' missing from 'CreateSession' return value")} - } - sessionHandleS, ok := sessionHandleV.Value().(string) - if !ok { - return nil, UnsupportedPlatformError{errors.New( - "unexpected 'session_handle' type in 'CreateSession' return value")} - } - sessionHandle := dbus.ObjectPath(sessionHandleS) - inVardict = make(map[string]dbus.Variant) - inVardict["type"] = dbus.MakeVariant(deviceKeyboard | devicePointer) - result, outVardict, err = getResponse(bus, remoteDesktop, - "org.freedesktop.portal.RemoteDesktop.SelectDevices", 0, sessionHandle, inVardict) - if err != nil { - return nil, UnsupportedPlatformError{err} - } - if result != 0 { - return nil, UnsupportedPlatformError{errors.New(fmt.Sprintf( - "Calling 'SelectDevices' failed (%v)", result))} - } - inVardict = make(map[string]dbus.Variant) - result, outVardict, err = getResponse(bus, remoteDesktop, - "org.freedesktop.portal.RemoteDesktop.Start", 0, sessionHandle, "", inVardict) - if err != nil { - return nil, UnsupportedPlatformError{err} - } - if result != 0 { - return nil, errors.New("keyboard or pointer access denied") - } - devicesV, ok := outVardict["devices"] - if !ok { - return nil, UnsupportedPlatformError{errors.New( - "'devices' missing from 'Start' return value")} - } - devices, ok := devicesV.Value().(uint32) - if !ok { - return nil, UnsupportedPlatformError{errors.New( - "unexpected 'devices' type in 'Start' return value")} - } - if devices&deviceKeyboard == 0 || devices&devicePointer == 0 { - return nil, errors.New("keyboard or pointer access denied") - } - cleanupBus = false - return &portalBackend{bus: bus, remoteDesktop: remoteDesktop, - sessionHandle: sessionHandle}, nil -} - -func getResponse(bus *dbus.Conn, object dbus.BusObject, method string, - flags dbus.Flags, args ...interface{}) (uint32, map[string]dbus.Variant, error) { - ch := make(chan *dbus.Signal) - bus.Signal(ch) - defer bus.RemoveSignal(ch) - var requestPath dbus.ObjectPath - if err := object.Call(method, flags, args...).Store(&requestPath); err != nil { - return 0, nil, err - } - for { - s := <-ch - if s.Path == requestPath && s.Name == "org.freedesktop.portal.Request.Response" { - if len(s.Body) != 2 { - return 0, nil, errors.New("unexpected 'Response' return length") - } - result, ok := s.Body[0].(uint32) - if !ok { - return 0, nil, errors.New("unexpected 'Response' return type") - } - outVardict, ok := s.Body[1].(map[string]dbus.Variant) - if !ok { - return 0, nil, errors.New("unexpected 'Response' return type") - } - return result, outVardict, nil - } - } -} - -func (p *portalBackend) Close() error { - p.lock.Lock() - defer p.lock.Unlock() - if p.bus == nil { - return errors.New("dbus connection closed") - } - if err := p.bus.Close(); err != nil { - return err - } - p.bus = nil - p.remoteDesktop = nil - return nil -} - -func (p *portalBackend) keyboardKeys(keys []Keysym) error { - p.lock.RLock() - defer p.lock.RUnlock() - if p.bus == nil { - return errors.New("dbus connection closed") - } - inVardict := make(map[string]dbus.Variant) - for _, keysym := range keys { - for _, state := range [...]uint32{btnPressed, btnReleased} { - if err := p.remoteDesktop.Call( - "org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeysym", - 0, p.sessionHandle, inVardict, keysym, state).Store(); err != nil { - return err - } - } - } - return nil -} - -func (p *portalBackend) KeyboardText(text string) error { - keys := make([]Keysym, 0, len(text)) - for _, runeValue := range text { - keysym, err := RuneToKeysym(runeValue) - if err != nil { - return err - } - keys = append(keys, keysym) - } - return p.keyboardKeys(keys) -} - -func (p *portalBackend) KeyboardKey(key Key) error { - keysym, err := KeyToKeysym(key) - if err != nil { - return err - } - keys := [...]Keysym{keysym} - return p.keyboardKeys(keys[:]) -} - -func (p *portalBackend) PointerButton(button PointerButton, press bool) error { - p.lock.RLock() - defer p.lock.RUnlock() - if p.bus == nil { - return errors.New("dbus connection closed") - } - var btn int32 - if button == PointerButtonLeft { - btn = btnLeft - } else if button == PointerButtonMiddle { - btn = btnMiddle - } else if button == PointerButtonRight { - btn = btnRight - } else { - return errors.New("unsupported pointer button") - } - state := btnReleased - if press { - state = btnPressed - } - inVardict := make(map[string]dbus.Variant) - if err := p.remoteDesktop.Call("org.freedesktop.portal.RemoteDesktop.NotifyPointerButton", - 0, p.sessionHandle, inVardict, btn, state).Store(); err != nil { - return err - } - return nil -} - -func (p *portalBackend) PointerMove(deltaX, deltaY int) error { - p.lock.RLock() - defer p.lock.RUnlock() - if p.bus == nil { - return errors.New("dbus connection closed") - } - inVardict := make(map[string]dbus.Variant) - if err := p.remoteDesktop.Call("org.freedesktop.portal.RemoteDesktop.NotifyPointerMotion", - 0, p.sessionHandle, inVardict, float64(deltaX), float64(deltaY)).Store(); err != nil { - return err - } - return nil -} - -func (p *portalBackend) pointerScrollFull(deltaHorizontal, deltaVertical int, finish bool) error { - p.lock.RLock() - defer p.lock.RUnlock() - if p.bus == nil { - return errors.New("dbus connection closed") - } - inVardict := make(map[string]dbus.Variant) - inVardict["finish"] = dbus.MakeVariant(finish) - if err := p.remoteDesktop.Call("org.freedesktop.portal.RemoteDesktop.NotifyPointerAxis", - 0, p.sessionHandle, inVardict, float64(deltaHorizontal), float64(deltaVertical)).Store(); err != nil { - return err - } - return nil -} - -func (p *portalBackend) PointerScroll(deltaHorizontal, deltaVertical int) error { - return p.pointerScrollFull(deltaHorizontal, deltaVertical, false) -} - -func (p *portalBackend) PointerScrollFinish() error { - return p.pointerScrollFull(0, 0, true) -} diff --git a/backend_x11_disabled.go b/backend_x11_disabled.go deleted file mode 100644 index 11a8234..0000000 --- a/backend_x11_disabled.go +++ /dev/null @@ -1,28 +0,0 @@ -// +build !x11 - -/* - * Copyright (c) 2018 Unrud - * - * This file is part of Remote-Touchpad. - * - * Remote-Touchpad is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Remote-Touchpad is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Remote-Touchpad. If not, see . - */ - -package main - -import "errors" - -func InitX11Backend() (Backend, error) { - return nil, UnsupportedPlatformError{errors.New("disabled")} -} diff --git a/bindata.generated.go b/bindata.generated.go deleted file mode 100644 index 33940e7..0000000 --- a/bindata.generated.go +++ /dev/null @@ -1,384 +0,0 @@ -// Code generated by go-bindata. DO NOT EDIT. -// sources: -// webdata/fn.js -// webdata/icon.png -// webdata/icon.woff -// webdata/index.html -// webdata/main.css -// webdata/sha256.js -// webdata/tex.png -package main - -import ( - "bytes" - "compress/gzip" - "fmt" - "github.com/elazarl/go-bindata-assetfs" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -func bindataRead(data []byte, name string) ([]byte, error) { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - - var buf bytes.Buffer - _, err = io.Copy(&buf, gz) - clErr := gz.Close() - - if err != nil { - return nil, fmt.Errorf("Read %q: %v", name, err) - } - if clErr != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -type asset struct { - bytes []byte - info os.FileInfo -} - -type bindataFileInfo struct { - name string - size int64 - mode os.FileMode - modTime time.Time -} - -func (fi bindataFileInfo) Name() string { - return fi.name -} -func (fi bindataFileInfo) Size() int64 { - return fi.size -} -func (fi bindataFileInfo) Mode() os.FileMode { - return fi.mode -} -func (fi bindataFileInfo) ModTime() time.Time { - return fi.modTime -} -func (fi bindataFileInfo) IsDir() bool { - return false -} -func (fi bindataFileInfo) Sys() interface{} { - return nil -} - -var _webdataFnJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xdc\x3a\x5b\x6f\xdb\x38\x97\xef\xf9\x15\xa7\x7a\x28\xe4\xc6\xb1\xe3\x74\x3b\x33\x1b\xd7\xdd\x75\x13\x67\x12\x34\x37\xd8\xce\xb4\x46\x10\x04\xb4\x74\x6c\x6b\x22\x8b\x1a\x91\x8a\xe3\x4e\xf3\xdf\x17\xa4\x2e\xa6\x28\x4a\x4e\x0a\x74\x1f\x3e\x03\x6d\x24\xf1\xdc\xef\xa4\x64\xc5\x0c\x81\xf1\xc8\x73\xb8\xd5\xdd\x69\xbf\xdb\x81\x77\x00\x00\x47\x34\x5c\x47\xde\x7c\xc1\xc1\x76\x1a\x70\xb0\xdf\xf9\x63\xef\x60\xbf\xf3\xdf\x70\x13\x44\xb1\x0b\x1f\x63\xf1\xe7\x7f\x69\xcc\x7d\x4a\x1f\x5a\x0e\x5d\x7e\xda\x81\x0c\x75\xbc\xf0\x18\xcc\x3c\x1f\xc1\x63\x10\x92\x88\x03\x9d\xc1\x10\x97\x94\xe3\xde\x98\xc6\xce\x22\x24\x6e\x6b\x03\xae\xad\x08\xa4\x59\x84\x08\x8c\xce\xf8\x8a\x44\x78\x08\x6b\x1a\x83\x43\x02\x88\xd0\xf5\x84\xa4\xd3\x98\x23\x78\x1c\x48\xe0\xb6\x69\x04\x4b\xea\x7a\xb3\x75\x4a\xcd\xe3\x10\x07\x2e\x46\xc0\x17\x08\x1c\xa3\x25\x13\xdc\xc5\xcd\x9f\x97\x37\xf0\x27\x06\x18\x11\x1f\xae\xe3\xa9\xef\x39\x70\xee\x39\x18\x30\x04\xc2\x20\x14\x4f\xd8\x02\x5d\x98\x66\xa4\x04\xd2\x89\x90\x64\x94\x4a\x02\x27\x34\x0e\x5c\xc2\x3d\x1a\x34\x01\x3d\xbe\xc0\x08\x1e\x31\x62\x1e\x0d\xe0\x7d\xc6\x26\xa5\xd9\x04\x1a\xa5\x74\x6c\xc2\x85\x0a\x11\xd0\x50\xa0\x36\x80\x04\x6b\xf0\x09\xdf\x60\xd7\x5b\x63\xa3\xb4\x0b\x5e\x20\x99\x2c\x68\x88\xc0\x17\x84\x0b\x7d\x57\x9e\xef\xc3\x14\x21\x66\x38\x8b\xfd\x66\x4a\x68\x1a\x73\xf8\x7a\x36\x3e\xbd\xba\x19\x43\xff\x72\x02\x5f\xfb\xc3\x61\xff\x72\x3c\xe9\xc2\xca\xe3\x0b\x1a\x73\xc0\x47\x4c\xa8\x79\xcb\xd0\xf7\xd0\x85\x15\x89\x22\x12\xf0\x35\xd0\x59\x4a\xe4\x62\x30\x3c\x3a\xed\x5f\x8e\xfb\x9f\xcf\xce\xcf\xc6\x13\xa0\x11\x9c\x9c\x8d\x2f\x07\xa3\x11\x9c\x5c\x0d\xa1\x0f\xd7\xfd\xe1\xf8\xec\xe8\xe6\xbc\x3f\x84\xeb\x9b\xe1\xf5\xd5\x68\xd0\x02\x18\xa1\x10\x0e\x53\x1a\x35\x76\x9f\x49\xef\x45\x08\x2e\x72\xe2\xf9\x4c\xb1\xc3\x84\xc6\xc0\x16\x34\xf6\x5d\x58\x90\x47\x84\x08\x1d\xf4\x1e\xd1\x05\x02\x0e\x0d\xd7\xdb\x9d\x9a\xd2\x21\x3e\x0d\xe6\x52\xe5\x52\x08\x02\x9c\xcd\x20\xa0\xbc\x09\x0c\x11\x3e\x2e\x38\x0f\x0f\xdb\xed\xd5\x6a\xd5\x9a\x07\x71\x8b\x46\xf3\xb6\x9f\x90\x62\xed\x4f\x42\xb2\xf6\xce\x4e\xbb\x0d\xb7\x1d\x90\x14\x9a\x70\x90\x5c\x20\x6b\xc2\xfb\xec\xf2\x0e\x6c\x11\x4c\xde\x13\xfa\x8d\x1d\x87\x06\x8c\xc3\xf8\xea\xe6\xe8\xf4\xfe\xe2\xea\xaf\xc1\xfd\xf8\x74\x38\x18\x9d\x5e\x9d\x1f\x43\x0f\x6e\x3b\xfb\x4d\xe8\x7c\x10\xff\xee\xba\x82\xf0\x05\x79\x02\xee\x2d\x11\xa6\xc8\x57\x88\x01\x08\x74\x74\x62\xee\x3d\x22\xf0\x84\xbc\xb4\x98\xe3\x7b\xce\x83\x17\xcc\x85\x37\xdc\x88\xcc\xe7\xe2\x5a\xb0\x5d\x7a\xbe\xef\x31\x74\x68\xe0\xb2\x22\xf7\xf1\xd9\xc5\x40\x04\x42\x0f\x0e\x3e\xec\x4b\x6e\xb7\xb7\x52\xc8\x76\x02\xde\x84\x65\xec\x73\x2f\xf4\x3d\x87\x70\x1a\xdd\x35\xa1\xd5\x6a\xdd\xa5\x24\xae\xaf\xce\x2e\xc7\x83\xe1\x7d\xff\xe8\x68\x70\x3e\x18\xf6\xc7\x67\x57\x97\x42\x81\x1d\x61\xdf\xdb\xfd\x26\xec\xdf\x35\x93\xeb\x3f\x7e\x6f\x42\x27\xbb\xe9\xfc\xfe\x5e\xb9\xfb\xf0\xe1\x7d\x13\x0e\xee\x76\xee\xba\x3b\x1a\xd9\xcf\x37\xe3\xf1\xd5\xe5\xfd\xf9\xe0\x44\xc8\xb7\xdf\x35\x2f\x0f\xcf\xfe\x3c\x15\xeb\x9d\x8a\xf5\x8b\xb3\xe3\xe3\xf3\x81\x50\x30\x67\xf0\x65\x30\xb9\xff\xeb\xea\xfc\xe6\x62\x70\x7f\x71\x33\x1e\xa8\xc4\x95\xa5\xe3\xab\xaf\x97\x2a\x5d\x65\xe9\xe6\x3a\xa1\xb7\x59\xb8\x18\x1c\x9f\xf5\xef\xaf\xcf\xfb\x93\xfb\xeb\xfe\xcd\x48\xd0\x7c\x6f\x58\x1f\x0e\xfe\xba\x1f\x0f\xfb\x47\x5f\xa0\x07\xff\x55\x5e\xbf\x1c\x7c\x1b\xe7\xeb\x1f\xd4\xf5\xcf\xc3\xab\xaf\x23\xa1\x54\xb2\xf6\x9b\x69\xed\xe4\x6a\xf8\xb5\x3f\x14\x21\xf4\xbb\xba\x3c\xba\xb9\x1e\x0c\xa1\x07\x7f\x74\x77\x76\x7c\xe4\xb0\x62\x5d\xf9\x37\x24\x6e\x7e\xe1\x93\x29\xfa\xe9\xba\x8c\xa8\x0b\x2a\x52\xaa\x07\x33\xe2\x33\xec\x6e\x9e\x8f\xb8\x28\xdb\xd2\x60\xf9\xb3\x73\xc2\xf8\x20\x70\xb5\xa7\x43\xf4\x91\x30\x74\x8f\x68\x1c\x28\x18\x34\x98\x53\x2f\x98\xa7\x59\x21\xa2\xe5\x2e\x59\x58\xd2\x47\xfc\x36\x8a\x97\x1b\x50\xf1\x64\x52\x78\xc2\x9c\x88\xfa\xfe\x37\xc3\xb3\x22\x5c\x1e\xfc\x05\x05\xb2\xa7\x63\x6f\x89\xa2\xcc\xf5\x20\x88\x7d\x5f\xa5\x52\x40\xd9\x99\xc5\x81\x23\xea\x32\xcc\x62\xdf\x67\x4e\x84\x18\x0c\x02\x32\xf5\xd1\xb5\x1b\xf0\xaf\x8c\xde\x08\x79\x1c\x05\x60\xbb\xd4\x89\x97\x18\xf0\x56\x09\x14\x7e\xfc\x90\x90\xe2\x97\x43\xad\x70\xfa\xe0\xf1\x93\x17\xc1\x2e\xe9\x77\x01\x38\xda\x0e\xc8\x6a\x09\x4a\xad\x1a\xdd\x9d\x67\x45\xb1\x08\xff\x89\x91\x29\x82\xd8\xa2\x3d\xc9\x66\xc4\x32\x15\xbd\x19\xd8\xd8\x2a\x41\x66\xcb\xe2\x67\x58\xb6\x33\x2a\x5d\x09\xf5\x0c\xe8\x33\x4c\x69\x25\xda\x0f\xeb\x29\x56\x00\xd5\xd1\x5d\xd2\xef\x0a\xfc\xc8\x40\xd4\x04\x51\x4b\x91\x6d\x91\xd2\x00\xa0\xd3\x2b\x18\x1c\x9f\x54\xb7\xdb\xaa\x8d\x73\x3f\x16\x61\x54\x7e\x15\x20\x76\x59\x74\x2d\xd2\x06\xdb\x49\x9a\x00\xeb\x08\x2f\xe9\xf7\x23\x12\x38\xe8\x9b\x6d\x5d\x07\x57\x4b\x96\xbd\x40\x56\x1d\xc8\x36\xda\x5a\x49\x45\x1f\x05\xde\x8b\xb2\x36\x01\x7d\x59\xd6\xd6\xc0\x16\xb3\xb6\x0e\x90\xd5\x12\x14\x15\x4a\x4b\x5a\xe2\xba\x1b\x14\x67\x41\x82\x39\x0e\x1e\x31\xe0\xe7\x1e\xe3\x62\xe0\xb1\xfd\xf4\xc2\x18\x5d\x65\x55\xdf\xf4\xe4\x60\x3c\xf3\x02\x74\x8d\xe6\x26\xae\x5b\x64\x60\xcd\x34\xfe\x56\x13\x72\xa6\xdb\x62\xb1\xac\xee\x4f\x08\x90\x90\xfa\x39\x31\x8c\xae\xf9\x09\x19\x96\xf4\xfb\x4f\x0a\x60\x70\xf9\x4f\xf0\xbf\x18\x6d\xc8\x1c\x55\xb1\x2f\x44\x8e\x98\x8f\x65\xe3\xb5\x79\x32\xa8\x8a\xa9\x72\xc4\xc9\x32\xd4\x32\x63\xc3\xdf\x73\x31\xe0\xde\xcc\xc3\xe8\x30\x69\xeb\xad\xcd\x93\x66\x0e\x15\x92\x39\x7e\xcb\x00\xe4\x8d\xb6\x26\xc7\x86\x1a\x80\x89\xba\x36\xd1\xd6\x4a\xc8\x0a\x40\xae\xc1\xe1\xe6\x32\xd1\xbc\x98\x34\xea\xd8\x71\x16\xb8\xf8\xf4\x79\x7d\xe6\xda\x9e\x3b\xa6\x27\x5e\x90\x5b\x5c\x0c\xd1\xb6\x18\x07\x3c\x39\x4b\x80\x07\x1f\xb5\x81\xa5\xe5\x63\x30\xe7\x8b\x2e\x78\xbb\xbb\xaa\x9f\x84\x83\x8b\x90\xb7\xde\x9d\x62\x2b\xe8\xf5\x40\xe7\x96\xfd\x52\xa3\x7b\xdd\xfc\xe9\xf3\xce\xe6\xff\x74\x75\xaf\x53\x54\xc8\x21\xbe\x13\x8b\xbd\xe2\x35\xf5\x02\x8e\x51\xdf\x71\xd0\xc7\x48\xee\x41\x2f\x62\x9f\xdb\x2c\x44\xac\x55\xcc\x34\xbd\x57\xa9\x27\x47\xa4\x03\xe8\x19\x91\x6e\xbd\xbb\xdb\xfd\xbb\x6e\x01\x98\xd4\x01\x77\x14\x60\x61\x38\x76\x00\x1f\x7b\x50\x10\x38\xfb\x39\x34\xe0\x5e\x10\xa3\x6e\x9b\x0c\xd5\x13\x86\xdd\xaf\xb0\x28\x39\x30\xa1\x49\x5d\x3a\x95\xe2\xc1\x1e\x74\x0c\xfa\x6c\x43\x50\x75\xca\xfa\x4b\xe2\x03\xd8\x03\xd6\x69\x40\x5b\xea\x29\xaf\x1b\xf0\x0e\x6c\x22\x6e\x48\xa7\x01\xbb\x40\x3a\x5d\xc5\xe1\x42\xad\x1a\xdf\xc0\xa7\xa2\xbe\x29\x33\xa3\x6c\x75\x64\x0a\x52\x17\x42\xad\xa3\xa7\xce\x71\x71\x70\xce\xfb\x68\xd5\x40\x2d\xd6\x56\xac\xc5\x30\x70\x6d\x6b\x6a\xc1\xae\x71\x4b\xb7\x0b\x56\x77\xdf\xd2\x9a\x5b\x1c\xba\x84\xa3\xd8\x7d\xf4\x03\x77\x24\x87\xf2\x9c\x5d\xbe\x4b\x80\x1e\x5c\x10\xbe\x68\xf1\x28\x0e\x1c\x3b\xdb\x38\xa4\x25\x2f\xdf\x39\x94\xa1\x26\x1b\x28\x61\x64\xb9\x4a\xa6\x2c\xa1\xd0\x80\x4f\x3d\xe8\xc0\x8f\x1f\x50\x78\x3e\x49\x9e\xab\x16\xcf\x77\x2a\x7b\xbd\xe4\xba\x5b\x58\x9a\x28\x4b\x93\xcd\x52\x6e\x90\xa5\x30\x48\xa2\x87\x30\x41\x76\x37\x69\xa8\xae\x50\x76\x3b\x45\x3d\x36\x5b\x20\x45\xdf\x74\x0f\x64\x82\xac\xd2\x39\xa5\x63\xd0\x3a\xc5\x2b\xeb\xad\xec\xbe\xf6\x7a\xd9\x5d\x57\x5b\x9e\x14\x96\x27\xfa\x72\xb2\xc3\xe2\x91\x9a\xd1\xb9\x65\x98\xb0\x45\xa6\x75\x66\x9b\x4c\x1c\x53\x43\x5b\x90\xc0\xf5\x51\xf6\x07\x1b\x1f\x79\x26\x6c\xbb\x0d\x17\xf2\x84\x72\x8e\x5c\x14\x4a\xb1\x13\x4a\x8f\x31\x50\xb6\x89\xe4\xb8\x84\x2f\x10\x18\x59\xe6\x67\x28\xb9\x91\x8c\x25\xbf\x54\x68\x0a\xfb\x61\x7c\xe4\xad\xbc\x01\x75\x8b\x30\xda\x5e\xba\xe8\x64\x9e\x6f\x84\x05\x89\x64\x8c\x70\x53\xce\xdd\xca\xda\xcd\xb7\x77\x23\xbe\x69\x43\x9c\x44\xc2\x12\x6f\x7a\x62\xab\x0f\x6f\xdf\x42\xd5\x9a\x3c\x06\x80\xb7\x6f\x0b\xc5\xf4\x65\xd6\x80\x2d\xc5\x5a\x68\x17\x46\x28\x46\x98\x63\x9c\x11\xd1\xa1\x1a\xc5\x12\x2b\x85\x82\x9e\x3e\xa5\x48\x31\x9b\x45\x03\x6b\x98\x9e\xfb\x04\xbd\x8a\x16\xaf\x0f\x2d\x8d\x62\xeb\x11\xa8\x1f\xcb\x9a\x68\x3a\x87\x31\x4b\xa5\x51\xd0\xd3\xd1\xae\x0e\xf1\xd6\x73\x9f\xee\x44\xbc\x8b\x5b\x93\x55\xca\xe7\x27\xaa\x70\x6f\xb2\x02\xab\x8b\xa7\x9c\x95\x54\x17\x42\x15\x78\x62\x04\x9e\x14\x81\x9f\x4d\x99\x5e\x5f\x7c\xa0\x98\xf7\xf5\xf5\x27\x53\x4b\x6f\x1b\x6f\x92\xbe\x51\x8a\x26\x1f\x49\x94\xb5\x1c\x0d\x47\x53\xb1\xae\x11\xe9\x30\xa5\xf2\x53\x1c\x28\xf2\x32\xa5\x8b\xb3\xa9\x52\x33\x4b\x63\x6f\x38\x3c\x2a\x12\x37\x55\xad\x41\xe0\xaa\x35\xeb\x97\xd5\x82\x17\x25\x88\x3e\xae\xbe\x2c\x4b\xea\xf2\x5d\xcb\x20\x16\xfa\x9e\x83\x82\x50\x13\x3a\x0d\xad\x40\x16\x8e\x0a\x77\x77\xbb\x55\xe9\x51\x51\x64\x7f\xa1\xdb\x40\x2d\xa6\xc5\x13\xcd\x4f\xc6\xf3\xfb\xd4\x0b\xa5\x3e\x91\xf5\x80\x4d\xe8\x3d\xbf\xa0\xdd\xe4\xb5\x5a\x63\xad\x35\x66\x35\xa9\x74\xfd\xcb\x47\xa1\x46\xeb\x6c\x9f\xd2\xca\x2e\x96\x25\x4a\xd1\xee\xed\xdb\xa2\x87\x60\x4f\xed\x91\x1f\x8b\x6f\x1c\x74\x39\x45\x98\x4e\x63\xce\x69\x50\xac\x83\x35\x1e\xe8\x69\x66\xc8\x7e\x39\x19\x83\x3e\x45\xc2\xca\xee\xdc\x4c\xff\xe0\x35\xf4\xe5\x9b\x88\x57\x32\x78\xff\x1a\x06\xc9\xab\x0c\x8d\x43\x8d\x3f\x53\x3a\xc2\x85\x1d\x3d\xf8\x85\x48\x19\x1f\xa3\xa5\x4c\x82\x95\xeb\x2c\x43\x9e\x95\xe8\xd2\x3e\xa1\xa9\x79\xdc\x68\x9a\x32\x93\x4a\x1d\xf6\x75\x1d\x9e\xab\x3a\xaa\xe1\xdd\x03\x54\x94\x61\x11\xba\x7a\x1d\x66\xf1\xf2\xdb\x06\x2b\x7d\x32\x29\x3e\xf9\x4f\xaa\xd5\x5a\x22\x9b\x52\xd3\xf5\x18\xcf\xda\x3b\xfb\x27\xe2\xc9\x46\x22\xa4\x2b\x55\x26\x79\xb0\x03\x7b\xa6\x21\xa8\xb5\x39\x06\x6a\x8a\xac\xda\x2d\xb9\xbd\x8a\xe0\xa4\x86\xe0\x24\x27\x68\x88\x6e\x73\x59\xad\x2d\xdb\xea\xc9\x6b\x1e\xf3\x42\x73\x33\xd6\xad\x99\x85\xd8\x59\x9b\x28\xd5\x14\xc7\x4f\xbd\xfa\xea\x08\xd5\x7d\xa4\x2e\x19\xa4\xe7\x9e\xb2\x01\xf4\x85\x4e\x2a\xce\xd6\xee\xba\x8c\x5e\xeb\x12\x6d\xa8\xf7\x96\x78\x8c\x3e\x27\x7a\xfb\xae\x20\x61\x68\xef\x32\x1b\x77\x7b\x42\x91\x77\xdb\x8f\xbd\xf2\x6d\xac\xfb\xd4\x80\xb6\xc2\xff\x1d\x74\xf6\xf7\xf7\x1b\x05\xba\x13\x49\x77\xfd\x3a\xba\xeb\x2d\x74\x2b\x0d\x6b\xf0\xc3\x76\xac\x89\xc1\xfc\xf5\x58\x1b\x13\x1b\x27\x26\x6d\xa8\xa9\x6f\xdd\xd9\xec\x55\x17\x9f\xb5\x23\x8c\x3c\x5a\xd8\xba\x8d\xd9\xed\x49\x2f\x57\xec\x5b\x92\xd5\x49\x69\xe7\x55\xcb\xb8\xd4\xb9\xb5\xd3\x8b\x12\x3b\xed\xf8\xa2\xc8\x30\xbf\x32\x9e\x4f\x19\xcf\xda\x17\xc4\x17\xd2\xe0\x10\x59\x48\x03\x86\xf6\x12\x19\x23\x73\x2c\x34\x9a\x05\xb9\x9a\xfe\x2d\xb6\x2b\xb8\x82\xbf\xd9\xe8\xb4\x6f\x5b\xa3\xd3\xfe\xde\xc1\x87\xdf\xac\x26\x58\xe3\xc1\xb7\x71\xd6\xf3\x12\xd0\x16\x43\x7e\x7a\xd1\x3f\xfa\x82\xeb\x8c\x9e\x19\x2e\x91\xd3\x5e\x79\x81\x4b\x57\x2d\x9f\x3a\x32\x92\x5b\x0b\xc2\x16\x2d\x16\x4f\x19\x8f\xec\x4e\x56\x34\xd3\x63\xbf\x29\xa7\xc4\x4e\xd1\xe7\x09\x1b\xdb\xfa\x3c\x19\x0f\x46\x56\x23\x39\xa5\x4b\xa9\x95\x5f\x45\xf8\x94\xb8\x56\x13\x32\xe5\x0b\x27\x76\x24\xe6\x0b\xd1\xaa\x1c\xc2\xf5\x53\x10\xf9\x3d\x40\x88\x41\x32\x9e\xe6\xaf\x3a\xe6\xc8\xd3\xd7\x22\xb2\xe9\x59\x29\x88\xa5\x1c\x78\x39\x3e\x65\x92\x5a\x25\x52\x02\x91\xe1\x84\xa4\x16\x38\x24\x2a\x64\x72\x0a\x52\x0f\x2e\x61\x54\x89\x1e\x70\xcd\xea\x70\xc4\xba\x06\x3f\xa5\x24\xaa\x15\x2b\x83\x51\xf1\x36\xaf\x9c\xf2\x29\xb1\x12\x5f\x87\x55\xe9\x70\x7c\xe2\x75\xb8\x62\x5d\xc0\x27\xa3\x4c\x16\xd5\x6c\x41\x57\x23\x07\x03\xb4\x99\xf8\x5f\xcd\xb1\xdb\xd4\x4d\xcd\xd4\x39\x4d\x61\xca\xa6\xb4\x4b\x33\xd7\xf6\xae\x35\xa3\xd1\x80\x38\x0b\x3b\x8f\x15\xd4\x13\x15\x5b\x8e\x4f\x18\x13\xd1\xd5\xe2\x74\x3e\xf7\xd1\xb6\x16\x9e\xeb\x62\x60\x35\x01\xe1\x4d\x0f\x12\xde\x4a\x7a\xe6\x19\x58\x96\xf6\x0b\xae\x99\xad\x97\x2b\xc3\xab\xe1\x92\x14\xc6\x37\xcc\xc5\x62\xb0\xb1\x86\xd0\x52\x9b\xc3\x16\x1e\xe3\x34\x5a\xb7\x18\x27\x5c\x4a\x9d\x86\x80\xc6\x27\x03\x0b\x63\x26\x66\x01\x8e\x69\xa8\x34\xc1\x2a\xef\xba\x2a\x14\x94\xa6\xfd\x7f\x51\x52\x72\x52\xb7\xef\xf8\xc4\x5b\x33\xea\xc4\xcc\x7e\x89\xfa\x69\x34\xbf\xc8\x04\x09\x6c\x9d\x19\x24\xf3\x47\xe2\xc7\x08\x3d\xb0\xac\xac\xfe\x65\xe2\xa6\xf1\x98\xc5\xf0\x8a\xa5\x75\xf6\x2b\x4e\x47\xd4\x79\x40\x6e\xe7\x64\xed\xbc\x40\x86\x11\xe5\xd4\xa1\xbe\x68\x21\x96\xfc\xf0\xcf\x82\xff\x01\x6b\xc5\x0e\x2d\x38\x14\x7f\xd9\xa1\xd5\x10\xbb\x91\x76\x5b\xec\x4e\x36\x85\x95\x32\x1e\x90\x25\x2a\x83\xad\x42\x94\x46\x5c\x50\x39\x2c\xa0\xc8\xa7\x87\x42\x3f\x49\x6f\xc5\x2c\x89\xba\x91\xb7\x45\x83\xb4\xc6\x8b\xb2\x99\xe7\xcb\x66\xaf\x92\xd9\xba\x50\x62\x0d\x07\x20\x32\x21\x6d\x6d\x44\x4e\xaa\xbe\xc9\xe3\x7a\xc5\x36\x9f\xd6\x97\x1b\x9c\x98\x20\x5c\xc2\x49\xa3\x36\x14\x7a\x75\xa1\x50\x0c\x68\x63\xc7\x37\x52\x2b\xe7\xd5\x26\xf7\x75\x22\x65\xb8\x24\x5e\x42\xe2\x96\x23\x4d\x75\x86\x34\xa3\xea\x8a\x5f\x9e\x71\x49\x25\xcd\xaa\x5b\x2a\x4b\x6d\x8b\xc9\x0a\xbd\xa1\x49\xcb\xaf\x43\xad\x66\x6e\x99\x94\xec\xd6\xee\xf3\x72\x92\x6a\x75\x90\x7b\x4a\xc3\x87\x73\xaa\x41\xf4\xfe\xa4\x14\x7e\xe2\xba\x79\xd5\x2f\xbc\x18\x2b\xe1\x54\x4b\xf5\x8b\xfc\x64\x8a\xa2\xf2\xf7\x6f\xb9\x59\xb3\x8b\x94\x55\x13\xfe\x0d\xc8\xa3\x37\x97\x35\xe0\xe6\xec\x10\x84\x96\x68\x3d\x97\x43\x2f\x7d\x72\xfb\xaf\xe7\x1e\x82\x35\x8d\xe8\x8a\x61\x34\x25\xce\x43\xea\x10\xd9\x55\x0f\x4b\x5f\x87\x3e\xa7\x1f\x47\x24\x68\x2c\x0e\x31\x2a\x23\xc8\x6f\x42\x8b\x90\x29\x83\x19\x8d\x56\x1b\xa7\x1b\x78\xa4\x5f\x99\x16\x91\xc3\x08\x1f\x79\x64\x94\x4d\xff\xf2\x55\x43\xf4\xc9\x3a\x24\x31\xc3\x4a\xc4\xfc\x93\xda\x22\x62\x80\x4f\xbc\x96\xe3\xe6\x5b\xda\x22\xe2\x23\xf5\xe3\x25\xba\x74\x15\x94\x31\x95\x2f\x7f\x4d\x48\xcb\x98\x1b\xe4\x54\xbe\x24\x36\x21\xc5\x61\x25\xca\xcd\xf5\xb3\x61\x20\xa2\xc6\xef\x7f\xb4\xec\xa4\x2d\xcf\xad\xcb\x48\x63\xec\x83\x7a\xba\xf6\x20\x9a\x11\x6d\x3d\xe0\xda\x34\x49\x6d\x2b\x0e\x82\xc6\x0b\x0a\x43\x65\x0a\x2a\xbd\x5b\xcc\x06\xa5\xf2\x9d\x8b\xc9\x85\x98\x1b\x68\xad\x89\x19\x47\x80\x62\x35\x4d\x37\x2d\x59\xdb\x10\x09\x64\x6b\x5a\xa6\x20\x34\x08\x69\x98\xb6\x95\x6a\xc9\xdf\x84\xc4\x55\x0a\x95\x43\x03\x4e\xbc\x80\x6d\xaa\x95\x7e\xf4\xf3\x26\x2b\xa4\xaf\xc6\x62\xb5\x18\xba\xc9\x5e\xd1\x1c\xa1\xd0\x20\x2b\x8f\xac\x5f\xd1\xb8\xa1\xa6\x79\x43\xdd\x69\x6f\x55\xff\x05\xc3\x91\xd6\xf3\x96\xa0\x8c\x50\x6c\x44\x7f\x36\x2c\xf3\xc1\x2c\x21\xa3\x07\x89\x70\x7b\x99\xa2\x3c\x25\x61\x9c\x44\xdc\x6a\xaa\x9f\x23\x6c\x47\xc2\xc0\xcd\x51\x06\x81\xbb\x1d\xc1\x91\x9f\xd1\xbe\x0e\x67\x49\x1f\x31\xc7\xb8\xa0\x8f\xf2\x03\xf0\x46\x77\xe7\xff\x02\x00\x00\xff\xff\xdc\x0d\xe5\x6c\xdc\x35\x00\x00") - -func webdataFnJsBytes() ([]byte, error) { - return bindataRead( - _webdataFnJs, - "webdata/fn.js", - ) -} - -func webdataFnJs() (*asset, error) { - bytes, err := webdataFnJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "webdata/fn.js", size: 13788, mode: os.FileMode(436), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _webdataIconPng = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x96\xe9\x3b\x14\xfa\xe3\x40\x3f\x63\x24\xb2\x5f\x4b\xb2\xa6\x14\x25\xa9\x64\x8f\x41\xb2\x6f\x59\x52\xb6\x68\x32\x2e\xb2\x94\x5d\x64\x64\xab\x84\xdc\xc9\xcd\x32\xc6\x50\xb6\x61\x48\x64\x1f\x4b\x62\x34\x64\xb9\x96\x21\xc6\x58\xc6\xa0\xc9\xce\x1d\x43\xc6\xef\xb9\x6f\x7e\xaf\xbf\xcf\x79\xce\xf9\x1b\xce\x4b\x3b\x1b\x53\xfe\x13\x92\x27\x00\x00\xfc\xe6\x66\xc6\xf6\x00\x00\xe4\x7f\x72\x73\x01\x00\xba\xf4\xbd\xfa\x00\x00\x5c\x0f\x2c\x4d\x8d\xc1\xd1\x7f\x60\x09\xa5\x28\x00\x4e\x88\x98\x1b\x1b\x3a\x46\xa2\x57\x29\xae\x8f\xa4\xc7\x64\x88\x9b\xeb\x39\x84\xe8\x85\xbe\x2a\xd5\x97\xab\xd9\x9e\x29\x92\xb9\x89\x9c\x86\x46\x48\x1c\xde\x02\x65\x26\xaf\xe2\x74\xce\xc0\x48\x45\x74\x14\x5b\x24\xee\x54\x57\xe4\x47\xc5\xe1\x82\xf0\x65\x16\x4e\xc6\x99\x2e\x17\x87\x3b\x5c\x0b\x5c\x70\xf6\x67\x38\x1d\x6f\x19\x88\xc8\x93\x64\x72\xf2\x8c\xfe\xb8\x1f\x21\xb3\x1e\xad\x3a\xd3\x87\xd6\x7f\xf4\x6b\xc2\x26\xdc\x67\x1c\x43\xc9\xbf\x77\x04\xb3\xbd\x4d\xef\xa7\x55\xa3\xa9\x03\xe8\x1c\x6d\x9f\x93\x89\x42\x46\xdc\xf2\xff\x53\xd6\x8a\xfd\x28\x5e\x67\x32\x27\x02\xb2\xf7\xf5\x8f\x1d\xbf\xce\x07\xcd\x43\xab\x5a\x8e\x9e\xc6\xcf\x3a\xe0\xb5\x03\xf7\x47\x45\x0e\x29\x94\x20\xf2\xa7\xda\x55\xf0\x3a\x6d\xc5\xee\xba\xf8\x11\xde\x84\x22\x28\xa1\xfe\xee\x82\x64\x21\xe7\x4f\xc1\x58\xbd\x9a\x94\xb7\xf6\x51\xba\xba\x13\x27\x3a\x26\xb2\x05\xc2\xba\x11\x47\x3c\xfb\xf4\xdd\x31\x45\x49\xc3\xce\x6d\x82\x1e\xc5\xf4\x57\x62\xc7\xb8\xa2\xb6\x64\x2a\xec\x4c\xfe\xba\x8c\xa4\x80\x15\xd1\x47\xae\x7e\x64\x7c\x8b\x2f\x98\x9d\x7b\xc4\xa5\xd1\xb5\x85\x92\x3b\xec\xb7\x51\xab\x31\x11\x84\x6d\x29\xb7\x58\x5b\xea\x77\x63\x30\x18\x4c\x44\x85\xfa\xa7\x5f\x8e\x5d\xa3\xcc\x49\x5f\x45\x37\xa2\xf3\x75\x6d\xbf\xb1\xde\x4f\x70\x75\xe9\xd0\x12\xd9\xe1\x3f\x2d\x1e\xf7\xe2\xd2\xbc\x81\xe8\xa3\x90\x12\xe9\x61\xce\x77\xae\xea\x1f\x43\xfc\x82\x22\xb0\x42\xc3\xd2\x32\x77\xf8\x69\x3f\xda\x3a\xcd\x81\x86\x16\x4f\x07\x97\x22\x64\x62\x56\x01\x48\xa1\x39\x3d\x13\x50\xe0\x51\x30\x1b\x71\xd2\xce\x99\x74\x9c\x2d\x44\x3a\x69\x0c\x15\x0d\xb1\x23\x25\x68\xe4\xb9\xe7\xf8\xd7\x79\x00\x3e\x18\xee\xbd\xdc\x67\x5f\xb5\xc4\xf7\x97\xda\xd6\x5d\xa2\x19\xde\xe2\xc3\x93\x9c\xfd\xb6\x85\x6a\x0e\xd8\xb0\xda\x8b\x61\x49\xb9\xd0\x37\x7d\xde\x39\x98\x1c\x43\x08\x21\x36\x36\xf1\xe6\x9c\xce\xdb\xcf\x26\xf9\x37\x25\x6a\x84\x37\x8e\x29\x37\x5c\xfd\xbd\x18\xfc\xe8\x07\x1f\x2b\x2a\x34\x86\xef\xd3\xcb\x9e\xb1\xe5\x6b\xea\x19\x90\xf9\x4d\xad\xe2\x07\x99\x0f\x6a\xcc\x1e\x20\x4a\x64\xd5\xc7\x6c\x58\x76\xae\xe1\xf0\x1d\x0f\x27\xc2\xb6\xf3\xba\xf8\x23\xa2\x44\xa3\xf8\x58\xec\xb1\xb2\x5c\x1f\xe2\xe7\xc4\xdc\x41\x90\x64\xe4\xb5\x32\xc8\xf2\x8e\x0b\x94\xf4\xc6\x72\x55\xdf\x4c\xd2\x21\x3f\x7b\xb1\x7c\x45\x02\xb2\xd3\x7f\x30\x30\xb6\x28\x8c\x69\xe7\xb5\x8b\xd5\x1d\x1f\x63\x59\x12\x57\xa6\x5f\xfd\x94\x7c\x82\x3d\x13\xb4\x19\x0a\x1c\x0f\xc4\x1d\x9f\xb6\x87\x10\x19\xb2\x49\x06\x79\x70\xe2\x36\xc7\x3a\x97\xc1\x85\x17\x01\x11\x7b\xd9\x5c\xa4\x15\x78\x33\xda\x27\x08\xea\xe4\xaa\x7a\x51\xa8\x7c\xc6\xc4\xb7\x2a\xfd\x7b\x0d\x21\x04\x53\x54\x1e\xa9\x2c\xbc\x30\xda\xda\xad\x9f\x57\x8f\x8c\xe3\x73\xaf\x53\xaf\xf8\x21\xd6\xa3\xd8\x03\x91\x58\x96\x2b\x8b\xba\x3f\xc8\x85\xfc\xb7\x0d\x97\xd7\xf2\x3c\x6a\x8d\xca\x92\xd7\x2d\xfa\x56\xdc\xde\x12\x40\xbb\xae\x9a\x1e\x44\x5f\x11\x4c\xb9\x90\x23\x66\x72\x68\x51\x78\x96\xf0\xe6\x06\xe3\xe1\xe7\xb0\xcf\x6f\xfa\xf0\x51\xf7\xd7\x3d\x38\xbe\x50\xc4\x34\x4f\x59\x6c\x77\x54\xd5\x88\xe5\x0b\x2e\xaa\x7d\xf9\x9e\x80\x9a\xd6\x7f\x82\xfb\x59\xe8\xa4\x45\x37\x7b\xe9\x31\xfb\x1d\x28\x25\x6d\x39\xb7\x51\xa9\xa7\xf2\xe0\x75\xca\x4e\x8f\x0f\x0a\xf1\x22\xc6\xbf\x8b\x49\x44\x66\xcb\xf2\xdc\x9d\xfc\x28\x35\x60\x82\xf4\xb3\x94\x6c\xee\x1a\x2b\xb0\xdd\xe3\xb3\xbe\xde\x68\xf7\xb8\x05\x37\xe7\xf5\x17\x39\x4a\x0d\x20\x6a\x7e\x22\xbc\x17\x58\x6d\xb8\x2d\x9a\x6d\x9e\xd3\x80\x80\x4a\x4f\x1e\x64\xa8\xc4\x9b\x7b\xbc\xd8\x91\xf5\x6b\xc8\xaf\xf5\x14\x30\x58\x5a\x62\x44\x6c\x44\xcb\x4e\x79\xdf\x90\x02\xff\x8c\x84\xe9\xd6\x55\x2d\xd4\x50\x7a\x6a\x21\x43\x2b\xe5\xfb\x56\xbf\x94\x1e\x7a\xca\xfb\x52\xaa\x6e\x45\x6a\x00\xd1\x90\x65\xda\x29\xa2\x60\x85\x88\x9c\xee\x33\x2b\xcd\xed\x40\x00\xf4\x0e\x5e\xbe\x87\x6a\xe4\x5b\x63\x47\x50\xe9\x32\xd5\xb7\x8d\xe6\x36\x72\x15\xe9\x91\x3a\x20\xfb\xd8\xce\xd7\x23\x03\xdd\xe0\xa9\x3b\xfd\x45\xa5\x33\xf7\x8e\x1e\xe7\x7a\x62\xe3\x90\xec\xf8\x1f\x54\x28\x92\x0c\x05\xcb\x49\xa2\x74\x5a\x1e\xa3\x25\x42\xf5\xe1\x15\x22\x55\xc9\x56\x82\xe2\x79\x43\x0e\x80\x6b\x3c\x3b\x39\x22\xfe\x2d\xea\x56\xb6\x5e\x18\xed\xdb\xb2\xbf\x03\x0c\x66\x62\xa5\x1a\x5d\xe4\xd3\xf4\x0f\x25\x43\xf9\xbe\x20\xfa\x9c\x86\x17\xd5\x88\x93\xcc\xd3\x9a\x53\xc8\xf9\xe9\x0a\x3a\xe0\xe4\x88\x2f\x8e\x8a\xef\x34\xe8\xd8\x7c\x3e\x6e\x59\x3a\x50\xe4\x73\x68\x50\x16\xdd\x52\x1e\x71\x5a\x91\x01\xf5\x13\x30\xb0\x0d\x52\x55\x1b\x19\xc1\x8a\xea\xe9\x11\x76\xcb\xca\x2f\xec\xeb\xf2\x07\xb6\x7e\x31\x75\xfe\xea\xf9\x64\x80\xb6\x28\x98\xfa\xe1\x09\xbe\x1d\x81\xa5\x83\xc4\x9b\x5e\xc9\xc9\x97\x2b\x4c\x55\xac\x43\xfa\xd6\x40\xc3\x3a\xff\xfc\x7d\x8d\x33\xfb\xfb\x18\x10\xb7\x3f\xc8\x11\x84\xf6\x5b\x3d\x79\x13\x7f\x95\xdb\x2d\x85\x61\xfa\x7b\x3a\xea\x90\x34\x70\x7c\x99\x5b\xf9\xd9\x46\x96\x50\xc2\x70\xea\x51\x2a\x87\xfe\xc4\x38\xfc\xa3\xba\x9f\x67\x64\x8c\xb6\x21\x83\x8f\x83\xbc\xfc\x77\x85\x72\x06\xdc\xe8\x59\x77\xbc\xcc\xb3\x74\x82\x7d\x9b\x9e\x63\x47\x67\x3e\x24\x70\x4a\x0a\xab\x62\xb7\xc6\xfa\xda\xfa\xad\xc6\x79\xbb\x75\x6d\x58\xad\x3b\x19\x06\x35\x3e\x3b\x06\xfb\xd2\xd7\x40\xad\x51\xed\x75\xd7\xe4\x7a\xa7\x23\x87\x6d\xa1\xcc\x76\x26\x78\x05\x4e\xaf\x55\xb8\xa6\xed\xd6\xcd\x41\x3d\xc9\xe3\x65\x9d\x99\x94\xac\x41\x71\x66\x8c\xbc\xf1\x50\xc2\x92\x03\x0f\xd2\x7a\x2a\xfa\x35\xae\xef\xf8\x59\xc3\x6e\xfa\x40\xea\x91\xee\x18\x2b\x20\x70\xb1\xb3\x5f\x23\x65\xb0\xc7\xb4\xbb\xa1\xb2\x60\x8a\xe3\xd5\x14\xf3\xd8\xde\xfc\x12\x47\x22\xf4\x0d\xc9\x17\xf3\xb4\xa1\x17\x36\xd3\xef\xf0\x64\x78\xe1\x38\x8f\x81\x4c\x22\xf4\x55\x01\x55\xd2\xe3\xf1\xf1\x61\xe1\x3b\xd3\xe1\xee\x48\xe5\x06\x89\xdf\xf4\xfb\xda\xd3\x5b\x89\x5e\x5d\x1e\x1c\xcf\xd9\x5b\x3f\x3f\x61\xbb\x27\x54\x42\xd9\x70\x99\x9d\x49\x58\x1a\xb5\x46\x35\xf8\xdb\x94\x29\x92\xe2\x1a\x1b\x13\x96\xf2\x54\x3f\x62\xad\xfc\x43\x67\x1b\x73\x51\xe8\x65\x5d\xc9\x59\x37\x02\x3c\x22\x60\x77\xb3\xe3\xbb\x97\x3e\xcc\xf3\xa8\xf6\x13\x1c\x1b\x8d\x0b\x17\xd8\x8f\x64\x13\x74\x6c\xfc\xf0\xf1\xde\xc0\x6a\xe4\xeb\x15\x97\x75\xe3\xab\x12\xea\x12\xaf\x5d\x1a\xd4\x44\x7f\xc9\x95\xaf\x8a\x07\xed\xde\x2c\x4e\xad\xd7\x5c\xee\x53\xb2\xaa\xfd\x67\xbf\x81\xf7\x12\xd4\xee\x52\x16\xb7\x86\x16\x08\x80\x74\x5b\x3b\xef\x35\xe1\xa8\x58\x15\x20\x37\x98\x7d\xe3\x20\x3c\x1e\xcd\xdb\x96\x51\x94\xdb\x9d\x2e\xd8\x48\xdb\xb6\xbf\xe8\x52\xe0\x7d\x09\xfa\xa6\x0f\x21\xb5\x3f\xbe\x9e\x2b\x2f\x67\xda\x2e\x5a\x54\xb1\x6a\x14\x59\xdd\xc4\x57\x45\xa1\x2f\x91\x07\x11\xe6\x2d\x91\x24\x36\x45\x33\x29\x1b\xa3\xb9\x12\x18\x61\xca\x71\xad\xa9\xd2\x5b\xaa\xcf\xc2\x61\xfb\x42\x68\x42\x9b\xb9\xff\xe0\x9b\x8c\xe3\x82\xc9\xce\xa1\xec\xa2\x39\xd9\x84\x02\xd9\x40\xb8\x43\x2f\x81\x39\x7a\x96\xc0\x75\x90\xc2\xd0\xac\xc4\xd5\xe7\xfa\xaa\xe3\xd5\x57\x1c\x17\x77\x33\x2d\x89\x75\x7e\x1b\x3f\xbe\xa1\xca\x67\x30\x33\xff\xa6\x0b\x30\xcc\xc5\x19\x85\xfd\x37\x28\xeb\xb0\x57\xb7\x2b\xf5\x2b\x4a\x27\xc6\x07\x9b\x32\x5b\x86\xd9\x24\x2a\x4d\xd8\xa6\xd5\x86\xb8\xe2\x83\xbe\x3c\x7c\x95\x95\xae\x9c\x23\xd5\x54\x5b\x75\x64\x72\x2d\x47\x21\xf3\x85\x22\x73\x7d\x8a\xec\x84\xcf\xcf\xbf\xcf\xa7\x9d\x65\xf6\xf5\x1b\x1e\xce\xdf\x2c\x53\x18\x12\x74\x2f\x5c\x1f\x09\xd3\x35\xdc\x41\xc1\xcb\x8a\x76\x57\x02\xde\x93\xbc\x6b\xfb\x07\xce\x03\x5c\x75\x02\x71\x26\x04\xb9\xe0\xe2\x7f\x5e\x51\xd1\xec\x1c\x2a\xac\x45\x9a\x93\x0e\xab\xa5\xdc\x31\x0a\xf6\x75\xf1\xff\xeb\x9b\x02\xca\x01\xf5\x27\xfe\xf6\x06\x7a\x4c\x7f\xbc\x5d\x92\xcb\x16\x94\x8c\xe7\x46\x9e\x19\x98\xdc\xac\x55\x58\xa2\x2b\xd1\xb6\xd3\x93\x0d\x4b\x2f\x75\x35\x3f\x4f\xc4\x5a\xf9\x5b\xcb\x17\xf8\x04\x06\xa6\x6f\xd6\xfa\x98\x29\xcc\xa7\x1e\xda\xff\x53\xc2\xf2\xa9\x38\x0c\x48\xbb\xc8\x7e\xba\x45\x83\x77\x30\xc9\xf7\xfc\x9d\xba\x46\xe2\x8a\x55\x1d\x9f\xfa\x20\x62\x28\x31\x42\xda\xc6\xb7\xdd\xef\x8e\x50\xdf\xc9\xc1\xb5\x98\x38\xd4\xa2\xf6\x4f\x74\xdc\x8b\x1a\xb3\xd8\xb8\x14\x07\xe5\xdd\x65\xfe\xa7\x4c\xf6\x10\x2e\x1f\x7f\x90\x75\xd7\x99\x9c\x11\x7f\x92\xec\xea\xa8\x5e\x9b\x74\x85\x37\xe9\x47\x41\xfa\x2d\x0b\x9e\x2a\x92\x34\xb1\x5a\x73\x06\xcd\xb6\x10\x6d\x1d\x92\x15\x80\x3d\x2c\xc5\xe3\xc1\xeb\xf4\xf4\xcb\x5f\x9b\x9b\x05\xa0\x19\x90\xb0\x9c\xfd\xa2\xcf\x0f\xce\x60\x32\x85\x2a\x6d\xcd\x95\x77\xff\xfc\x80\x5f\xdd\xa8\x72\x6f\x41\x1f\x6c\x2e\x2f\xda\xa0\xb5\x1e\x7f\x4e\x7e\xaf\x14\x66\xd6\xb5\x48\x2a\x2c\xc8\x62\x48\xc0\x6c\x94\x58\x90\x64\xfb\x1d\xa6\x31\xf7\x31\xbf\xf6\x82\x81\xbd\x66\xd6\xf5\x18\x68\x02\x6f\x90\xb0\x0a\x26\xbc\xa8\xb2\x39\x78\xeb\x0f\xac\x97\x47\x67\xa9\x4b\xf9\x02\xce\x31\xfa\xc1\x11\xfb\x50\x1d\x1f\x14\xf2\xdd\x21\xcb\x16\xd3\xc6\x34\xe4\xcf\x30\x60\xc5\x9b\xc6\x45\x24\x7e\x3c\x59\x3a\x31\x51\x18\x65\x22\xb3\x47\xc1\x7f\xbf\x0c\x75\xdb\xd1\x69\x94\x60\x44\x4a\x8f\x2a\xdb\xc9\x4e\xda\x28\x5c\xa3\x6d\xad\x45\xc2\xc7\x49\x6e\x22\xc8\xe1\xb3\xed\xf7\x5f\xbc\x7d\x95\x82\x8c\x4b\x48\xc4\xde\x76\x13\x01\x48\xf6\xba\xb4\xa5\xa9\x45\xf1\x59\x33\x6e\xc9\x1e\x27\x08\xe0\x31\x7c\xdf\x74\x19\xd8\xa9\x70\xce\xb8\xc5\xcc\x21\xaa\xf8\x80\xe3\x09\x43\x17\xa3\xd9\x2b\x64\x2e\xa0\xc0\x99\x36\xfb\x13\x20\x13\xb1\x33\x6e\x72\xe0\xae\xf0\xf3\x77\x06\x46\xb3\x0a\xb6\x9c\x80\x24\xaf\xb4\xf2\x45\xf3\x6f\xd8\xa5\x5c\xab\x0f\xe4\x9d\x35\xec\xe4\xa7\xa4\x79\x0c\x82\xfc\xb1\x57\x0d\x02\x24\x08\x8c\x15\x1a\x2b\xe5\x96\x2a\x24\xdb\xa5\x9a\x75\xcc\x5e\x2d\xcb\x6a\x44\x7d\x3b\x4f\x4f\x0c\xdc\x6d\x96\x8e\x49\xf3\x96\x97\xc8\x78\xca\x10\xbf\x8a\x52\x69\xa6\x9d\xd4\xd4\x93\x00\x8d\x5a\x9c\x1e\xcd\xd2\xa2\x16\xe7\x27\x63\x0c\x39\x3e\x7a\x54\x10\x39\x3d\x9a\xa4\x15\xfe\xd2\xb6\x12\x21\xc7\xc8\x11\x7c\xa0\x60\xf1\xaa\xa1\xf9\x92\x60\xb5\x40\xbc\xab\x9c\xd4\xe0\xc0\x52\x2f\xed\x04\xb2\x1e\x06\xad\x1f\xb8\x21\x03\xca\xae\x5f\xb1\x42\x91\x1a\x7d\xc2\xdf\xf8\xfa\x21\x06\xbc\xb5\xde\xe9\xb4\x57\x9a\x87\xb4\x36\x89\xb9\xcf\xb2\x40\xc7\xb9\x40\xe1\xf8\x9c\xa6\x4b\x3e\x08\xba\x79\xc9\x36\x8d\xf7\x7c\xc9\xe4\x84\x18\xeb\x9c\x03\x4a\xf9\x6d\xb6\x35\x7d\xef\x91\xce\xad\x8b\xae\xb2\x7b\xca\x7b\x4a\x99\x02\x84\xa1\xa1\x07\xb8\xe0\xef\xaf\x4f\x2b\xbe\xd6\xdb\x5d\xb5\x29\x38\xa1\xcf\xbb\x5f\x82\x20\xdb\xc7\xf7\x8c\x52\xa7\x9f\x31\x51\x71\xfe\xe1\xf2\xa9\x2e\xf3\x7c\x1f\x97\xbf\x64\x23\xdf\xe5\xb9\xf7\x15\x2b\x15\xc7\xcd\x95\x0c\xa4\x88\xf3\x02\xb4\xa0\x61\xe5\x02\x32\x99\x08\x69\x88\x91\x1a\xd4\xc6\x8f\x46\x98\xb6\xed\xd2\x54\xf3\xdc\x7b\xfd\x54\xc2\x78\xee\xae\xbe\x2f\x5d\xa3\xa4\xcd\x97\xd9\xef\x03\x17\x77\x81\xd1\x88\x24\x86\x05\xca\x62\x3b\xd3\x77\x83\x69\xd7\xf9\x7b\x61\x48\x2f\xee\xa5\xe6\x06\x55\xfc\xff\x47\xab\xb1\xf7\x08\x92\xf1\xb6\xbe\x3e\x54\x6c\xca\x10\x00\x00\xcc\x6f\xd9\x18\x57\x19\x79\x3e\xff\xbf\x00\x00\x00\xff\xff\x9a\xb9\x5e\xe2\x5e\x0a\x00\x00") - -func webdataIconPngBytes() ([]byte, error) { - return bindataRead( - _webdataIconPng, - "webdata/icon.png", - ) -} - -func webdataIconPng() (*asset, error) { - bytes, err := webdataIconPngBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "webdata/icon.png", size: 2654, mode: os.FileMode(436), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _webdataIconWoff = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x98\x65\x50\x14\x7a\xb8\xc6\xff\xcb\xc2\xc2\x12\xd2\x2d\x2d\xdd\xb9\x20\x20\x9d\x87\x5a\xa4\x41\xe9\x10\x05\x96\x06\xa5\xbb\x24\x5c\xc2\x05\xc4\x05\x44\x6a\xe9\x54\xe2\xd0\x22\x92\x12\x4a\x09\xd2\x08\xd2\x48\x88\x70\xe7\x9c\x73\x3f\xdd\xb9\xef\xcc\x33\xef\xcc\x3b\xcf\x97\xe7\x9d\xe7\xd3\x2f\xd0\x50\x53\xd3\xd0\xc4\xc4\x10\x00\x6a\x23\x40\x02\x00\x00\xdc\x31\x00\x02\xfe\xdf\x51\xd3\xd4\x64\x07\x00\xda\x07\x00\xb9\x0c\x00\x6c\x16\x25\x2d\x71\x0c\x9a\x9a\x26\xfa\x00\x50\xd3\x00\x00\x6e\xff\xa3\xe7\x6f\x72\x6d\xb5\xd4\x35\x34\x01\xa0\xba\x02\x00\x30\xfc\x7b\xa7\x05\x34\x86\xf7\x45\x25\x00\x80\xb8\x01\x00\xf4\x00\x00\x76\xb2\x49\x8e\xfd\x8e\x1e\xf6\x28\x00\xf0\x0e\x00\x00\x6d\x00\xe0\xb1\x16\xdb\x8e\x9d\xb9\x39\xdb\x3b\x01\x00\x21\x02\x00\xc8\xfc\x23\x3a\xea\xde\x50\x37\x37\x67\x7b\x00\x20\xca\x00\x00\x56\x00\xc0\x1d\x42\x05\x28\xc2\xcd\xc3\x2f\x08\x00\x6a\x31\x00\x00\x0b\x00\x80\x13\xae\x0e\x11\xf1\xb0\x0f\x42\x01\x00\xb1\x03\x00\xc0\xfe\x15\xb9\x11\xf0\xb4\xf7\x70\x06\x00\xd2\x08\x00\x44\x01\x00\x3c\x8a\x97\x4b\x35\xb6\x28\x2f\x5f\x3f\x00\xa0\x38\x00\x00\x1d\x00\x80\xfd\xa6\x15\x48\xfe\x9b\x19\x02\x40\x3b\xa7\xbf\x8b\x2d\xb9\xc2\x29\x20\x81\x6e\xfd\x93\xf9\x0b\x4e\xdf\xf7\x9f\x3d\xef\x28\x31\x02\x28\x6f\xf4\xa0\x53\x50\x52\x00\x00\x11\xc0\xfb\xef\x27\x41\xf9\x8e\x76\x4e\x76\x76\x1b\x17\xfb\xbd\x14\xfd\x57\x51\x10\xa2\x97\x35\x62\x62\x12\x58\x3c\x1e\x60\xc9\x00\x7d\xf6\x8f\xc3\x08\x00\xf2\xff\x7c\xf6\x61\xdd\xb2\x91\x91\x59\x91\x91\xe5\xc9\x8f\xc8\xa1\x50\x35\xf2\x18\x97\x90\x2e\xbb\xc1\x41\x7c\x1a\x67\xbb\x75\xa7\x08\x28\x4f\x1c\x94\xc4\x6e\x37\x42\xea\x0b\x4d\x1c\x34\xa9\xed\xe6\xc6\xd5\xec\xdc\xab\x9b\xa2\x7f\xb8\xb7\x96\x80\x6f\x98\xc8\x1c\x07\x62\x52\xc0\xd1\xe2\xad\xa5\xa0\x7c\xe7\xf4\x0e\xcf\x1e\x31\x9a\xd8\x34\x0e\xa3\xf7\x26\x3f\x17\xfa\xb9\xb4\xf5\xd7\x9c\xf0\x62\x04\x4c\xee\xbc\xa9\x36\xc1\x56\x18\x13\xd2\x14\x45\x44\x30\x60\x82\x96\xe1\x77\x03\xb7\xb9\xfc\x76\x1b\x0e\x89\x14\xbd\x87\x3f\x5b\xd5\x8f\x05\x56\xfd\xf6\x7d\x02\x10\xe7\x91\xca\x1d\xac\x07\x1b\x96\xec\x5d\xa5\x9b\xb0\xea\x83\x9f\xef\xe0\x89\x0d\x9b\x20\xb2\xf5\x6c\xa1\xa7\x2a\xe8\x68\x61\xe3\x8f\xbc\xa4\x06\x6a\x64\x6b\xda\xd4\x54\x68\xbc\x3b\x7e\x13\xb6\x19\x16\xec\xe9\xa0\x47\x2e\x4d\xb8\x95\x20\x3f\x26\xac\xcd\xa1\xd8\x12\x7c\x13\x05\x97\xba\x25\xdb\x6d\x17\x7d\xfb\x71\xa6\xdf\xa4\x46\x12\xab\x74\xa4\x6a\xca\xbd\x47\xa9\x51\x8c\x74\x82\x47\x09\x4f\x13\x07\x70\xcd\xb8\x59\xa4\x74\x73\x82\x45\x79\xdd\x07\x2e\xb1\x89\x78\xf4\xec\xda\x8e\x71\xc0\x64\x81\x9e\xc9\x42\xce\x71\xcf\xd7\xac\xdb\xb3\xba\x03\x73\xd9\xf2\x55\x2d\xc8\x8a\xb3\xe1\xb0\x80\x58\x02\x8e\x69\x05\xd7\xc1\xad\x81\xfa\xfd\x17\x68\xb1\x4f\x3e\x6d\xd6\x54\x6f\x45\xca\xfd\xdc\x5f\x66\xc8\xe5\x50\x86\xea\x92\xbe\x13\xf5\xf8\xc1\x37\x00\x38\xfe\xc4\xef\x71\x4f\x0f\x98\xd0\xaf\x9b\xe4\x29\xa0\x75\xde\xac\x19\x99\xad\xf2\x1d\xb2\xef\xc6\x27\xea\x4e\x29\xf1\x92\x8c\x8a\xc9\x1e\x05\xfe\xe8\x1c\xfa\xa3\xb3\xd6\x98\x9b\xa4\xe1\x92\xca\x2c\x83\x14\x48\xcd\x34\x3e\xf2\x8a\xb7\x75\x2f\x35\x54\x0f\xca\x5f\x1f\x53\xbc\x15\xa5\x4c\x41\xb0\xdc\x12\xc1\xee\x96\x52\x27\xd6\xac\xda\x63\x58\xbb\x4c\x53\xd0\xfd\x82\x22\x6d\x85\x72\x57\xd8\xed\x84\xf4\x61\x34\x85\x74\xdf\xe7\x54\xa3\x89\xaf\xcb\x62\x8e\xf6\x29\x08\x14\xda\x8b\xf7\x77\x3d\xe6\x15\x81\x18\xc0\xa2\x69\xc5\x07\x6f\x25\x05\x94\x11\x94\x24\xf9\xb2\xdc\xda\x84\xc9\x98\xb8\xbe\x34\x2e\x12\x1e\x7b\xb8\x25\x1d\x46\xb5\x96\xc1\x5c\xea\x3d\xe3\x0f\x5c\x4f\x5d\x03\x6c\xcd\x5b\xdc\xc3\x84\x45\xe7\x8f\x90\xf9\x49\xc5\x27\xd7\x56\x8e\xdf\xce\xf6\x27\xcb\xf6\xb9\x2e\xe7\x1f\xf2\x61\x91\x55\x3a\x5c\xc7\x97\x89\x11\x70\xfc\x77\x11\x04\xea\xd5\x5d\x14\x09\xf7\x21\x79\xee\x3c\x5b\xa2\x15\x4d\x16\x37\xd9\xee\x1e\x2b\x07\x34\x3f\x82\xed\xba\x62\x0a\x64\x0e\x7d\xbf\x86\x5b\x89\xba\x19\x08\x15\xff\xdb\x21\x17\x10\x7d\x63\xcd\x98\x12\x41\xc3\x05\x65\x02\x40\x68\x12\xb2\xf3\x4f\xb7\x3a\x2c\xe1\x41\x26\x1a\x54\xd5\x08\xf4\x47\x3d\xd5\x41\x1d\x3c\xa2\x35\x75\x11\x88\x8e\xec\x9d\xa1\xc3\xa8\x18\x35\x0d\x15\x24\xdd\x95\x11\x25\x05\x85\x83\x8d\xbe\x2c\xa7\x88\xd7\x03\xe5\xe1\x88\x05\xc1\x1c\x41\x2c\x8e\x33\x29\xa1\x35\x82\x21\xa8\x88\x0d\x47\x01\x47\xb8\xab\xaa\xa7\x92\x43\xa4\x23\xc4\x25\x75\x5c\xde\xb6\x5c\x87\x1d\xf3\xc4\xf4\x2e\xb7\xec\x1c\x1d\x9e\x9a\x7c\x79\x30\x3d\xbd\x38\x2d\x4a\x4c\x40\x90\x51\xc4\xd7\x7c\xcd\xd5\xf0\x9a\xa3\x5f\x95\xb8\x29\x50\x2b\xbd\xad\x69\xc5\xd3\xd1\x7b\xc9\xc5\x1f\x41\x9c\xcb\xf2\x16\x57\xb6\x67\x2b\x5d\xed\x93\x55\xc5\x1f\xcc\x99\x4b\xa5\x21\x63\xca\xa4\x25\x5d\x42\xa7\x9f\x47\x9a\xf6\x96\x6f\x50\x96\x2f\x6a\xa5\x8c\x51\xba\x74\x9e\xef\xf9\x67\x9d\x92\x4e\xa9\x08\xd6\xa2\xc2\x13\x59\xb9\x47\x22\xc2\xc3\xa1\x2e\x36\x56\xee\x35\x0f\xcf\xb7\x5f\xce\x98\x5e\x3b\xfd\x92\x09\x93\x78\xfe\xfa\xde\xf9\xf6\xe7\x3a\x68\xd8\xde\xe0\x56\xc2\xeb\x93\x46\xa9\xab\x93\x1f\x9f\xbb\xcf\x75\x3e\xe9\xff\x9c\x7c\xd7\xcb\xe2\xa3\x97\xe3\x08\x4b\x63\xdc\x0e\xdd\x1b\x0d\x0b\x18\xf2\x5d\x1d\x48\x19\xd1\x79\x22\x5d\xef\xef\x60\x15\x3f\x55\x9d\x48\xae\x74\x6c\xb3\x57\xd6\xe7\xf9\xee\x1c\xd9\xe0\xeb\xcb\xe3\xe4\x9a\xe6\xa5\x24\x21\x5f\x49\x88\x97\xd5\x4d\xc5\x2f\x2a\xd8\x90\x69\x9e\x19\xd2\x5f\x3e\x6c\xb6\xc1\x57\x23\xe0\x9b\x15\x3c\x14\xe2\x1c\x59\x6f\x64\xa9\x4e\x99\x20\x38\x5b\x3f\xf5\x7e\x52\x66\x94\xd5\x5d\xd4\x47\x3d\x6e\x2c\x71\x81\xd6\x23\x79\x7b\xb1\x8b\x5e\xbe\x16\x79\x66\xfd\xe2\x15\xc1\xde\x30\x5e\x09\x16\xeb\x73\x3f\xb2\x89\xbd\xba\x22\xe3\x59\x55\x0d\x12\x15\xe9\x6a\x8c\x93\x6f\xf0\x41\xf3\x4e\xb2\x8a\xa8\x07\xaf\x3e\x8a\x82\x09\x30\xaa\xf2\xcf\x4d\x92\x8e\x07\x68\x8c\x97\x8e\xc4\x8b\x6b\xcf\x57\xa9\xca\xe5\x69\xf4\x48\xf8\x3c\x67\x12\xec\x9b\xc0\xd3\x27\x5a\xb2\x33\x4d\x18\xbb\x44\x53\x28\x9d\x39\x95\x7d\xed\x6d\x25\x89\x55\x08\xe5\x4d\x2a\x0c\xdd\x61\x68\x71\x6e\x12\xe9\x1b\x76\x5a\xe4\xef\x3b\x0a\x10\x48\x8b\xa7\x31\x35\x32\xe1\x97\xf4\xb0\x1e\x31\xca\xcf\xda\xb6\xdd\x77\xe2\xf3\xaf\x85\xa9\xc4\xbd\xbd\x9d\x13\x95\x8b\x45\xc6\x0f\xf2\x97\x55\xa5\xf5\x97\x9f\xa1\x16\x29\xde\x64\x2f\xfa\xf3\x6f\xd9\x7d\x74\xd8\x18\x5a\x28\x99\x8d\x91\x0d\x4e\x97\xce\xaa\x5a\xf9\x5e\x39\x6f\x31\xb9\x3e\x82\x7b\x33\x05\xef\x65\x8b\xb7\xb3\x91\xcc\xd5\x90\xd7\xaa\xfd\xbe\x42\x68\x79\x6f\x15\x3e\x62\x57\xea\xf2\x73\xd0\xae\xb4\xb1\x97\x89\x59\x77\xc8\x5e\x69\xbb\x80\x98\x76\x17\x17\xf6\xd5\x67\xda\xae\x74\x63\x5f\x72\x0a\x27\xfc\x49\xbd\x07\xd3\xd8\x8b\xab\x0b\x53\x1a\xe0\x56\x24\xef\x7e\x36\xaa\x82\xfa\xaa\x2a\xd3\xfe\xe1\xc3\xc9\x06\xf3\x57\xcc\x4f\xe6\xe5\xcd\x98\x0f\x16\xed\xa7\x7e\xe7\x96\xa7\x22\xe8\xcc\x47\x7a\x15\x3d\xcb\x72\x69\xea\xee\x77\xe0\xc3\x96\xcd\xb1\x9b\x6d\xb9\xe9\x55\x30\xc1\x41\x26\x92\x80\x82\x17\x3f\xed\x69\xf1\xa7\x39\x32\x2f\x73\x30\xed\xfc\xd9\xfe\x2f\x7e\x1a\xd8\x21\x1a\xc8\xe6\x41\x6d\xbc\xe4\x1b\x3d\xd8\xb3\xbb\x72\xf8\x8b\x9f\x52\x05\x4e\xcd\x23\xef\xc3\xf9\x0e\xf3\x1d\x0d\xf2\xde\xe6\xb2\x3e\x1a\xe6\x0f\xe8\xaf\x73\x72\x0a\xb7\xa8\x99\x23\xe1\xa6\xb6\x0f\x22\x23\xff\x16\x27\x26\x4e\x33\xd9\xb0\xac\x70\x29\xfb\x5b\xae\x33\x97\xf0\xbb\xa3\x52\x4c\xc4\x70\x67\xa0\x2c\xa3\x9a\x2e\xc6\xd7\x85\xb9\x79\x32\x90\x1d\x9d\x53\xea\xeb\x19\xe3\x7a\x77\x06\xeb\xce\x80\xee\xd3\x89\x51\x8c\x26\x44\xaa\xa6\x1e\xf4\xf7\x8b\x7c\xf2\xcc\x29\x89\xf8\xb0\x85\x18\xe2\x5e\xbe\x51\x9a\xce\xca\xb1\x61\xe5\xa2\x22\x6f\x35\xf4\xe9\x7f\xd5\x99\xc6\x3d\x1a\xfa\xe6\x92\xb0\x80\x27\xc8\x67\x17\x56\xb0\xa2\xc7\x29\x7d\xa1\xfd\xbc\xa6\x9d\x41\x08\x02\xf5\x22\x55\xd0\x47\x49\xfe\xdd\xc7\x8c\x33\x4e\xe8\x5f\xed\x60\x53\xa0\xc8\x63\xf8\xf1\x83\xc6\x5c\x32\x1f\x9e\x4a\x69\xf5\x02\x5e\xa5\x47\x7c\x2a\x53\xb6\xe7\x27\x8d\x0e\x3f\x68\x37\x5b\xe4\x9a\x79\xd5\x1b\x82\x9b\xca\x1e\x53\x33\x12\x79\x5c\x3d\xaa\xe5\xe9\x7b\x1a\x63\xd2\x76\x75\xea\xa8\xe7\xd7\xce\xcb\x7b\x4e\xdc\x9e\x19\xe9\xd7\xc3\xf8\x9b\xd6\x0a\x6f\xeb\x1c\x65\xff\xb3\x75\x27\x1f\x9b\xa5\x53\x63\x11\x7b\x9a\xcc\x26\x65\xff\x71\xbe\xd2\xdb\xe1\xa1\xd0\xb4\x45\x06\xc9\xa9\x08\xfe\xe3\xca\xbe\xb8\xcc\xe8\x7c\x89\xee\xef\x34\x9d\x6a\xa2\x6e\x3d\x88\xc7\x18\x81\xcc\x6a\x16\xa3\xb8\x6f\x9b\x16\x35\xdc\x78\xb1\x07\xd2\xf3\xc6\x73\x76\xc8\xe6\xa3\x22\x24\xd7\x43\xa9\x1c\x74\xf9\x14\xf5\x1c\x6e\x7c\x46\x03\xd1\x24\xa7\xe3\x9e\x87\xef\x91\xec\x37\x33\xa3\xe3\xfe\xac\x00\x6f\x6e\xcb\xca\xa3\x81\x1f\xab\xdf\x61\x95\x77\xe0\xee\x28\x75\x0b\x43\xa3\x79\x25\x91\x94\x2c\x12\x42\xf0\xf2\xc9\xae\x8a\x36\x25\xc9\x9c\xe8\x41\xa7\x11\xcf\x73\xf3\x12\x71\x86\x31\x0d\x6e\xdf\xfe\xdc\xab\x9a\x49\xff\x73\xad\x1d\x09\x93\x03\xc3\x8c\xab\x37\x4c\x96\x45\xcb\x23\xd7\xf8\x19\xc5\xaf\xbf\xc3\xf7\x33\x72\x5e\xc8\x26\x53\x71\xc8\x71\xa3\xc5\x07\xf9\xa9\x6f\xf7\x48\x78\xc0\x9f\x09\x3c\x40\xa8\x97\x50\x95\x1b\x08\xb8\x18\x68\xb5\x62\x61\xfa\x6f\x2f\x72\x12\x8d\x73\xf1\xb3\xb7\x11\xf4\x7c\xe9\x31\x37\x73\x3a\xa5\x46\x73\xa3\x53\x29\xe7\xa1\x6d\xc9\x14\x72\xf5\x9c\x0b\x64\x26\x5c\xf7\x69\x6c\x4a\x98\x66\x8b\x6b\xd7\x73\xd7\xc6\xbe\xa4\x5c\x5d\x06\xec\x77\x24\x85\xc7\x32\x2d\x7f\x21\xbb\x9e\xd3\x19\xe4\x76\xd4\xe7\x3f\xff\x6b\xda\xc2\x75\xa0\x72\x60\x8e\xc4\xdb\x4c\xf2\x87\xca\x79\x6a\x99\xe6\x73\xa2\x7d\x8d\x54\x35\xb3\x5b\x29\xdd\xe7\x42\x53\x63\xb4\x73\x42\x97\xfb\x33\xcd\x7a\x59\x85\xaa\xf3\x27\x4c\x1e\x08\x1b\xf6\xbf\x92\xc2\xd9\xe0\x43\x22\xaf\x9c\x44\x4c\x7d\xec\x47\x8c\x59\xfa\x8d\x59\x5c\x9e\x65\xa1\xc5\x15\x70\x18\x41\xdf\x93\xcb\xd3\x0a\x5b\xeb\xf0\xb9\x30\x03\xf7\x31\xd5\xb5\xc9\x82\xd8\x74\x1a\x1b\xe7\x53\x56\x87\xd0\x3d\x82\x8a\xfc\x86\x9d\x49\x4f\xc4\xbb\x98\x16\xde\xca\xfa\x37\x0d\x95\x05\x72\x5f\xd3\x7f\xd6\xc6\xa2\xae\x92\x5e\xb7\xde\x13\xa2\x34\xb5\xb2\xf1\x29\xc3\xca\x95\x9c\xaf\x7f\x7a\x7f\x02\x6f\x96\x16\xdb\xf6\x8a\x4a\x75\x0a\xa5\x8e\xc8\xd6\xd6\x7e\x84\x52\xb5\x78\x46\x7b\x94\xfa\x31\x65\xca\xf1\xbe\xea\xc5\x16\x99\x35\x56\xb7\xeb\xa8\x48\x97\x14\x85\xe1\x69\xda\x98\x0f\x71\xbf\xe3\xdd\xe5\x40\x77\x01\xa5\xba\x60\xa8\x48\xf7\xe2\x65\xa7\x97\x38\x94\x7c\x7b\xef\xf6\x69\x29\x08\x6e\x02\xa9\xf1\x3f\x42\x97\x7b\xe9\x4e\xf4\x64\xb2\x63\xe2\xbf\xd4\xaa\x9e\xaa\xa0\xd4\x54\x08\xb0\xb6\x62\x36\x99\xb2\x7c\x65\x5d\x07\xa2\xa4\x5e\x91\xce\x82\x44\x95\x62\x8b\x91\xdb\x3b\x9f\xc9\xb6\x23\x6f\x2b\xb7\xda\x44\xaf\xde\xa5\x59\x59\x42\xd9\x0b\xf3\x3d\x13\x6f\x27\x93\xcb\xac\xb6\x80\x6e\x99\x2f\x87\xf5\x34\x92\x8e\x9f\x45\x08\x88\x5d\x31\xa4\x5b\xc0\x3a\xff\x74\x09\x73\xdc\x44\x25\xf7\x8f\xef\x13\x14\x52\x4d\xba\xeb\x85\x9b\x8e\x94\x09\x32\x1e\x8d\xf4\x0e\x39\x45\xa2\x52\x4b\xa7\x3f\xd4\x59\xbd\xc6\xca\xae\x22\x79\xe9\xb6\xb9\x93\x84\xb5\xe3\xca\x61\xa8\xd6\x24\x78\x47\xa2\x45\xa2\x5f\x60\xc4\x48\x16\x7b\x20\x9e\x27\x2d\x16\x25\x56\x00\x9b\x22\x92\x21\x87\x24\x81\x53\x29\x2d\xbd\xfd\x1b\x4c\xcc\xfe\xfc\x45\x3d\xd3\x04\x6a\xdc\x21\x1d\xa7\x40\x6a\xd5\xb3\x79\x38\x40\x5d\x7f\x5e\xd8\x9b\x03\xd3\x45\x08\x0d\xe9\xd8\xb4\x3f\xa4\x7f\x2a\xfa\x85\x8e\x86\x95\x29\x65\xa8\x93\x29\x80\x48\xd7\x18\x43\x64\x9a\x0d\x19\xbf\x4b\xe7\x7a\xb6\x2a\xfb\x60\xdc\xde\x62\x29\x69\xa5\xd3\xfe\x40\x6f\xd6\xde\x29\x72\xad\xb7\x4a\x2a\x4e\x2e\x9a\x2d\xa2\xcc\x03\xc1\x24\xe9\xce\x31\xc6\x1b\x33\xa5\x86\x20\x8e\xf5\x3e\xc8\x79\xf2\xb4\x16\xc9\xad\xee\xe1\x2c\x43\x69\x68\xdd\xf7\x73\x42\x5f\x9b\x58\xf3\xd3\x7b\xbc\xea\x47\xca\x1a\x8d\xe1\xfd\x09\x05\x04\x7e\x8c\xad\x4f\xa6\x2d\x94\x8b\x70\x11\x13\x76\xfc\x90\x91\xc2\x26\xef\xcd\x88\x3c\xe2\xc4\xfb\xe9\x83\xd1\xd9\xab\xb5\xe2\x3a\x67\xaf\x29\xd5\xa5\xd3\xdd\x55\xbf\xbe\xbc\x32\xac\xce\x13\xf2\x61\xaf\x72\x5a\x59\x13\x91\x2b\xcc\x15\x09\x21\xf9\x11\x44\xc4\x16\xfd\xdd\x3b\x17\x79\xac\x4e\xc6\x7e\x48\x82\x55\x57\xf6\x34\x72\x54\xfe\x03\x1b\x25\x8d\xef\x43\x2e\xb3\xc6\x66\x47\x77\x91\x65\xd1\xc2\x6c\x64\xcb\xd6\xbb\xa4\x4d\xa0\xb2\x6e\xb3\x55\x98\x99\x7b\x39\x3b\x04\x6e\xf2\x65\xc5\x25\x93\xac\x2f\x74\x10\x49\x39\x7c\xea\x95\xb1\xbf\xf1\xd2\x77\xbb\xfc\x94\xd5\xb0\xb5\x60\xdc\xce\x48\xf4\xa3\x3f\xf0\xa3\x33\xf8\x80\xae\xb3\x66\x24\x4c\x7c\x35\x1e\x48\x8e\x92\x8f\xd8\x21\xb4\x81\x9c\x4b\x98\x4a\x0e\x5f\xd6\x06\x40\xac\x1c\x4e\xc1\xad\x29\xff\x74\x8d\x40\xd1\x71\xd6\xcf\xec\x6e\xc1\xd4\x56\x7b\xe8\xf9\x56\x2a\xed\x3c\xf7\x5d\x49\xce\x78\x97\xf7\xa2\xde\xfb\xd1\x87\x9e\x09\x38\xb4\x7e\x8c\x7b\xc5\x36\x6c\x4b\xe1\xbc\x87\x03\x89\xd7\x19\xb7\x2c\x5f\x93\x3b\x7b\x49\x34\x65\x22\x7b\xcc\x51\xd6\xa2\xb6\x02\x5d\x14\x1a\x20\xc1\xce\x40\xf7\xe4\x25\x7a\xa5\x50\xe9\xa1\x64\xe9\x4f\x4c\xe8\xd4\x6c\xb5\x94\x8f\x39\xe6\x0d\xee\x0a\x9b\xf2\x2a\x8f\xfc\x15\x31\xa7\xb6\xa6\xa6\xfc\x1c\xb3\x1b\x9c\x49\x5d\xda\x68\x5d\xe1\x28\x2f\x12\x61\x44\x2c\xbb\x89\xb6\x5c\x3a\xc6\xd6\x8b\x93\x16\xe0\x4b\x33\x4c\xfd\x49\xbf\xc5\x4d\xed\x0a\xdd\x53\xd3\x56\x57\x76\x88\x76\x71\xf1\x85\xa2\x0a\xb1\x05\xec\xc7\xe9\xfc\x77\xee\x27\xf7\x7d\x57\xa7\x38\x76\xe2\xa3\xc2\x1b\x85\x8c\xf8\x4a\x87\x2f\x39\xed\xd2\x5b\x1e\xdf\x13\x06\xe5\x06\xdf\x1a\x5f\x6b\x1c\x71\x1c\x07\x33\x2e\x45\x67\x93\x19\x3a\xe6\x10\x92\xb9\x20\x97\x8c\xf5\x83\x66\x5c\x8e\xf8\x1b\x9e\x09\x92\xe0\xda\xd3\xf6\xab\x1a\x03\xea\x6b\xde\x27\xa0\x0b\x29\xac\x02\xea\x57\x67\x78\x82\xf0\x2d\x77\x19\xca\x4d\xed\x6e\xca\x22\x3d\x16\x8d\x37\x05\x2d\x70\x87\x1d\x83\x08\xd3\xa1\xbf\x7a\x24\xc2\xe6\x6b\xa8\x56\x4a\x5d\x66\xfa\x25\x79\x7c\xf6\x29\xe0\x96\xb1\xce\x83\x1a\xa6\x16\x32\x02\xad\x39\x35\x1e\x45\xd4\x41\x5c\xb7\x27\x77\x83\xac\x0e\x28\x0d\x54\x04\xdc\x82\xcc\xe7\xe2\xba\x97\x59\xa5\xf4\xff\x0a\x5c\x81\x48\xb8\x29\xa4\x7e\x84\x27\x3c\x60\x80\x8a\xa6\xf2\xd2\x7d\x08\xc0\x61\x92\x87\x9e\xe3\x7a\x9f\x3e\xcc\x91\xcd\x7e\x82\x1e\xb9\x66\xc3\x8c\x89\x32\x0f\xb8\xc3\xe4\x09\x96\x0c\xaf\xd1\x3d\x4f\x55\xea\xa3\x7d\xff\x70\xc2\xbe\x88\x6e\xf8\x25\x96\x9a\x7d\x32\x2f\x98\x6d\x63\x1e\x51\x64\x0a\x52\x59\x34\x1e\xef\x7c\x30\x3f\xcf\x4b\x54\x32\x31\x59\x5f\x7e\x17\xf4\x11\xe1\x1f\xc2\x5e\x5e\x72\x0e\xc5\x32\x89\x26\x8b\x76\x17\x13\x8f\x89\x3e\xec\xac\xaa\xa9\x8c\x78\xdf\xa4\xca\x34\x38\xa0\xd5\x5a\xf6\xad\x3b\x0c\x76\x48\x9d\xf5\xda\x53\x3b\xd8\x35\x00\xa9\x30\xcd\xb2\x79\x75\xa9\x91\x62\xf1\xb7\x51\xf5\x3b\xf1\xc1\x65\x93\xf0\x05\x09\x11\xc6\xc2\x94\x2c\xef\x5b\x18\x08\x86\x24\x4f\xc9\x5c\x82\x5e\xc2\x5c\xe2\x71\xd7\x21\x18\x55\xa1\x26\xc2\x68\x4e\x0b\xb1\x88\xd4\x90\xf9\xd6\xa5\xd8\x2c\xcf\xf6\x0a\x9a\x15\x11\xd8\x86\x8b\x87\xba\xd2\x7f\x9a\xa9\x50\xa4\xb8\x51\x8e\x08\x2a\x31\xf7\x52\xc9\xa4\xf6\xe3\xcd\xdd\x59\x79\x8d\x0c\xd3\xc9\xe5\xcc\x6c\xe9\x31\x69\x70\x54\x2b\x76\xca\x7f\xe8\x56\x10\xb3\x8f\x75\x97\x32\xe9\x69\x9b\x7f\xd2\x10\xf2\x54\x4b\x88\x34\x57\x14\xc9\x5f\x6b\xbc\xbe\xff\x48\xd3\x37\xee\xdd\xdf\xce\x92\xad\xee\x4d\x5d\xb9\xb3\x78\xc7\x1e\x76\x8d\x5d\x7e\x0e\x79\xb3\x38\xe6\x91\x20\x7d\x36\xcf\x5f\x1b\x0b\x75\x96\x92\x3f\xc2\x90\xd8\xe2\x9a\x1d\x0d\x8e\xf6\x82\x9f\x1b\x24\x63\xb6\xa5\x10\x9e\xfb\x07\x02\xaf\xcf\x0f\x85\xad\xcd\x89\xc3\xcc\x25\xe7\x2c\xa5\x58\x9b\x26\x5d\xdf\xfd\x6e\x13\x64\x7a\xf8\x04\x5e\xfc\xdd\xb4\xe6\x51\x90\x86\xaa\xd7\xa4\xce\x2a\xed\xfd\xe3\x08\x71\x36\xe9\xa6\x7c\x1a\x44\xc8\xfa\x72\x95\xd5\xa6\xe0\x66\x52\xdf\xc5\x8e\x78\x97\x13\x71\xee\x3d\x46\x44\xe3\xdf\xf0\x2f\xd7\x5b\x3c\xf9\x93\x88\x67\xdb\x12\x85\x0b\xe1\x07\x12\x6c\x66\xe2\x39\xb1\x46\x06\x0b\xfe\x33\x02\x99\x93\x21\x04\xd6\x47\xbf\x8c\x9e\xf8\x7a\x8e\x23\xe2\x6a\x4f\xbe\xbe\xab\x17\x58\xa7\x0c\x87\xcf\x17\x93\x8f\x1a\xe5\xdf\xfb\xb6\xf7\xfe\xb6\x6c\xcc\x8b\xd0\x14\xa1\x0b\x5d\x56\xfa\x11\x53\x39\xd9\x09\xfa\x06\x92\x66\x87\x07\xa3\xc1\x0f\x8e\x8f\x30\x8f\x35\x6f\xc5\x25\xc2\xf0\xaf\xad\x3f\x2a\x61\x97\x28\x69\xc3\x63\x87\x5b\xba\xa4\xb5\x72\x30\x9f\xe1\xb9\x24\xde\xa8\xd9\xa4\x42\x39\x82\xe0\x89\xab\x5d\x27\x41\x24\x37\x64\xee\x50\x6a\x2c\xaf\x77\x25\xd8\xe0\x97\xe7\x80\xaf\xcf\x97\x6c\x6d\xaf\x2b\x89\xad\x04\x52\x45\xf6\xf3\x26\xc7\x5a\x46\x19\xc6\xbf\x06\xca\x75\x39\xc2\x27\x48\x5b\x5e\xb2\xc4\x4a\x72\x99\x4b\xea\xaf\x31\xa7\x8f\x24\x90\x41\x6b\xd4\x07\xa5\x46\x12\x58\x7c\xe3\xf5\x60\x8f\x0d\x24\x25\x1d\xde\x14\xaa\xf6\xba\x48\x4b\xe2\x57\xc9\xe5\x62\x32\x6e\x1f\x3d\xa6\xfd\x76\x67\xc6\xbd\xbb\x94\xd7\x50\x2d\x8e\xa6\x71\x60\xfa\x5c\x75\x40\x7f\x38\x4b\x90\xfe\x41\x83\x4f\x14\x27\x9b\x1e\x8d\x54\x14\xb3\xd5\x61\xdd\x4f\x53\x15\x47\x44\x07\x45\xb1\x6d\x53\xfe\xec\x02\x7f\xf2\x66\x25\xff\xaf\x99\xf1\x97\x7f\x84\x55\x1d\x0d\x1a\xd7\x96\xcc\x0a\x11\x35\x48\xa3\x31\xca\x0b\x9e\x93\xb5\xb0\x38\x17\xd3\x3e\x9a\x99\xe6\xa2\x61\x55\x41\xef\x90\xbb\x49\x03\xbc\xf2\xdc\xed\x0f\x43\xd4\xbe\x5f\x3f\x35\x88\x14\x65\x6e\x2d\xa5\xbc\xa2\x6b\xec\x77\x8d\x4a\x2f\x4a\x3a\xa3\xac\xba\x4a\x38\xbf\xab\xb6\xee\x9b\xfe\x85\xe4\x6b\xd6\x95\x59\x61\x6a\x59\xb6\x61\x2d\x7a\x55\xab\xfe\x64\xce\xb8\x1d\x6a\xe6\x6a\x1f\x42\xb2\x9f\x81\xae\x64\x6c\xab\x96\x24\xd9\x7b\x0c\x33\xa0\xaf\x20\xec\xc0\xc8\x33\xf2\xde\x78\x8c\x07\x31\x31\x37\x50\xc1\x60\x45\xb8\x4b\x85\x91\xb5\xfb\xcf\x91\x9c\xdd\xe1\x1f\xe0\xc9\xe4\x8f\x03\x6c\x42\xe9\x94\xcd\xfc\x7c\x9a\xd9\x82\xe6\xde\x19\x7d\x40\x38\xce\xa0\x65\xa3\x4a\xd3\x6e\xd0\xf0\x6c\x6e\x05\x85\x26\x5b\x84\x57\x81\xfa\x2c\xc7\x9d\x8a\xeb\xcf\xdf\x45\x30\xc2\x0b\x54\x1a\xbf\xe6\x48\xe1\x4f\xc9\xa0\xd6\x21\x2f\xd6\x3f\x74\xfd\x0a\xac\x62\x38\xeb\x53\x7e\x1b\x26\xc7\x38\xc0\xca\x15\x4e\x86\x5f\xe2\xb8\x3e\xdf\x5b\xce\xcf\x87\x91\xac\x35\x4a\xfa\x91\x9b\x50\x33\x1a\x8b\x3e\x5c\x3e\xf5\x27\x9c\x89\xae\xe4\x84\x96\x87\x92\x6f\x67\x57\x1d\x12\xdd\x09\xde\x11\x29\x78\x6d\x99\x7c\xa7\x0d\xdb\x77\x3f\xe1\xf6\x89\xa6\x38\x95\xde\x15\x5c\x67\xc0\x40\x77\xb7\x7f\x9c\xbe\x5d\x8d\x9f\xa9\x7c\xfe\x17\xcb\xe7\xb6\x0b\xf1\xdd\x55\xc3\x7a\x4c\x07\xe9\xe2\x66\x40\xa7\x6f\xbf\xce\xee\xef\x07\x62\xc8\xe7\x5f\x6a\xdb\x14\x5f\x5c\x49\x50\x0b\x0c\xb0\x7f\xe4\x17\x3b\xb8\xb5\x97\xad\xd7\xd8\x50\xfa\x2d\xdb\xd5\x0c\x36\x4a\xcc\x6c\x70\x12\xdf\xf5\x8d\xf4\xcf\x00\x69\xc1\xdc\x61\xc6\x1d\xee\xb4\xc2\x7b\x5b\xd4\x0e\x28\xdd\x7b\x07\xfc\x7b\x67\xdd\x81\x1c\x8f\xd3\xda\x3f\x8e\xe6\x2b\x0d\xbc\x51\x59\x6a\x68\xf6\xca\x11\xef\x58\x12\x1e\xa4\x48\x99\x3d\xa0\xac\xae\x20\x42\x04\x53\xfd\x7a\xfd\x24\x6b\x28\xa3\xd0\x75\xe2\x7b\xf8\xf6\xa4\xd1\x2e\xb7\xcd\xae\x92\x4f\x5d\x0e\xf6\x6b\x7f\x47\x3b\xfe\x90\x8b\xc2\x13\xd8\x0a\x72\x90\x82\x2d\x92\xec\x5c\xd6\x4c\xe6\x78\x7e\x21\xcd\xef\xd1\x42\x0d\xf7\x4d\x57\xbe\x59\x4e\xea\xb1\x28\x2d\xe1\x23\x8c\xfb\xad\xf2\x3e\xf3\xab\xde\x36\x53\x79\xbb\x97\x69\xea\x53\xce\x09\x15\x09\x2b\xb1\x47\x9e\xe6\x2b\xd1\x4b\xef\xc9\x9b\x68\x34\x36\x6a\xd3\x98\xa1\x3c\xdb\x01\x1f\x5d\x88\xb5\x27\x4c\xf6\x2a\x14\xa0\x01\x5b\xde\x10\x6b\x3f\x35\x6b\xa4\xc9\xfc\x0c\xcb\xe5\x7e\x8b\x46\xef\x85\x6a\x9d\xac\xea\x54\x65\x54\x31\x53\xf4\xfb\x45\xf6\x45\x27\x94\xc6\x1d\xb2\x8a\x26\x32\x5d\x5b\x2f\x8a\x05\x7f\x8f\x45\x13\xd4\xdf\x1e\x82\xed\xee\xee\x50\x57\x9c\xa4\x50\xa3\x6c\xa3\xdd\x21\x73\x7f\xf5\xc3\x75\x7b\xfb\x6a\x83\x53\xe2\x99\x6a\x0b\xb1\x67\x9b\x90\x2f\x5b\xfa\xf6\xa7\x79\xc6\xc9\x0c\xc4\xdd\xb6\xb9\xac\xc5\x55\xba\x4a\xeb\x2f\x9d\xb3\xd6\xa3\xa6\x3d\x59\xbc\x16\xbd\x37\x86\x06\x9a\x47\x0d\x97\xca\xff\xde\xf9\x14\xd8\xa1\x64\x7c\x7a\xb3\xd3\xb6\xab\x1f\xaa\x50\x6d\xee\x2a\x38\xb8\x37\xb5\xe8\x1c\xea\x2f\xf9\x80\xae\xed\x7e\xce\xd8\xe9\x8f\xcb\x13\x81\xa7\xcb\xd1\xcb\x83\xb6\xc1\xa8\x05\x43\x03\xeb\x07\x05\x17\x49\x73\xc8\xc5\x59\x05\xd3\x24\x91\x9f\x07\xdc\x3c\x8a\x26\xbf\x94\x2e\xd8\x9f\x6b\x87\x08\x53\xf2\x5f\x2f\x6c\xf5\xda\xf2\xa6\x70\xb1\x79\x60\x18\x92\xb4\xef\x75\x2b\xa2\x36\x18\xac\xe0\x88\x7b\xc2\x41\xab\xb5\x0d\xc5\x8c\xdb\x4c\x51\x6d\x6b\x5a\x05\xef\xdb\xd4\xa8\x35\x14\x7b\x0e\x20\x21\xcc\xf0\xe0\x8c\xc2\xd0\x09\xd9\xbd\x33\x4f\x2e\x2d\xc5\xeb\xc5\x34\xa5\x8c\x77\x5e\xf6\xef\xa2\xa1\x0c\xef\x63\xc8\x07\xb5\x5a\x55\x68\xbf\xb9\xaa\x9c\x93\xa4\x72\x86\xec\xf8\x7d\xef\x7f\x12\x8e\x9f\x78\x5a\x11\xe9\x18\x2e\xcc\x74\xe7\x19\xa3\x41\x2a\xde\x16\xb9\x5a\x9d\x43\xed\x32\xf9\xab\xd4\x38\xfc\xdf\xe2\x8a\x8c\x13\xce\x77\x1f\x13\xf7\xd3\xa2\x36\x05\x1a\x8d\x17\x2b\xbc\xaf\x5c\x0f\x56\x1f\x64\x6a\xd5\xf6\x2a\x6a\x7e\x27\xde\x1a\x32\xde\x3e\xae\xb3\xd1\x20\x9d\x4d\x47\x57\xd0\xa3\x04\x42\xa2\x69\xe7\x6a\x86\x6a\x78\x53\x9f\xde\x40\x56\x10\x5c\xb7\xff\x97\xdd\x44\x00\x56\x18\x25\x8c\x8e\x7d\x04\xbf\x2f\x85\x82\x14\xe0\x7d\x02\x02\xff\x91\x20\x00\xe6\xb2\x98\x58\xff\x2f\x03\x0a\xca\x77\x0c\xeb\xee\xea\xba\x8a\xb2\x27\x8f\x26\xa2\x11\x71\xa6\x70\x1a\x74\xa6\x88\x53\xa3\x04\xae\x07\xf8\xc1\x00\x80\xff\x09\x00\x00\xff\xff\x34\xc3\x2c\x05\x50\x13\x00\x00") - -func webdataIconWoffBytes() ([]byte, error) { - return bindataRead( - _webdataIconWoff, - "webdata/icon.woff", - ) -} - -func webdataIconWoff() (*asset, error) { - bytes, err := webdataIconWoffBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "webdata/icon.woff", size: 4944, mode: os.FileMode(436), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _webdataIndexHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x52\x3d\x6f\x14\x31\x10\xed\xef\x57\x38\xae\x93\x2c\x41\x02\x51\xd8\x6e\x12\x1a\x1a\x10\xa4\xa1\xf4\xd9\x73\xb7\xe6\xbc\xb6\xb1\x67\xf7\x72\x1d\x15\x08\x84\x44\x43\x41\x45\x10\x20\x84\x90\x02\xf4\xfc\x9b\xfc\x02\x7e\x02\xda\x8f\xbb\xbd\xec\xe5\x4e\x20\x51\xad\xe6\xcd\x7b\x7e\x6f\x66\x96\xed\x9d\xdc\x3f\x3e\x7d\xfc\xe0\x2e\xc9\xb1\xb0\x62\xc4\xea\x0f\xb1\xd2\x4d\x39\x05\x47\xc5\x68\xc4\x0a\x40\x49\x54\x2e\x63\x02\xe4\xb4\xc4\xc9\xc1\x1d\x2a\x3a\xd8\xc9\x02\x38\xad\x0c\xcc\x83\x8f\x48\x89\xf2\x0e\xc1\x21\xa7\x73\xa3\x31\xe7\x1a\x2a\xa3\xe0\xa0\x29\xf6\x89\x71\x06\x8d\xb4\x07\x49\x49\x0b\xfc\x68\x9f\x94\x09\x62\x53\xc9\xb1\x05\x7e\xa3\x7e\x35\xa9\x68\x02\x92\x14\x15\xa7\x13\x77\xf8\x24\x51\xc1\xb2\x16\x1c\x74\x53\x2e\x6f\xde\xba\x3d\x64\xa0\x41\x0b\xe2\x21\x14\x1e\x81\x9c\xfa\x52\xe5\x41\x6a\x96\xb5\xf0\x88\x59\xe3\x66\x24\x8f\x30\xe1\xb4\x90\xc6\x1d\xaa\x94\x28\x29\x40\x1b\xc9\x69\x52\x11\xc0\x51\x12\xc1\x72\x9a\x70\x61\x21\xe5\x00\x48\xaf\xca\x8c\xf2\xee\x30\xb8\x29\x25\xb8\x08\xc0\xa9\x29\xe4\x14\xb2\x06\x68\x84\x75\xbf\x59\x9b\x36\x15\x31\x9a\x53\x1f\xc0\x19\x37\xa5\x62\x44\x08\x21\xcc\xf9\x2e\x2b\x0b\xe2\x9e\xac\xe4\xa3\x76\x24\x93\x48\x84\xa7\xa5\x89\xa0\xf7\x58\x16\x04\xcb\x56\xc4\x56\x17\xc4\xb1\x77\x0e\x14\x1a\x37\xbd\x7c\xf6\xa5\xe6\x8c\x58\xa6\x4d\x25\x7a\x2b\x65\x7d\x02\x4d\x89\xb2\x32\x25\x4e\x73\xa3\x75\x73\xc3\xee\x81\x13\x93\x54\xfb\x06\xe8\x46\xdf\xe0\xe3\x12\xd1\xbb\x46\x1f\xc1\x7a\xa9\x5b\x80\x8a\xcb\xe7\xbf\x58\xd6\x16\x9b\x56\x41\x6e\xf5\x59\xf6\xad\x1c\x83\xa5\xa2\x3f\xc2\x35\x8e\x33\x58\xa4\x95\xdf\xbb\x9f\xbd\xdf\x90\x38\x29\xad\x6d\x0f\xb4\xa2\x7f\xfe\xb8\x9d\x3e\x83\xc5\xd8\xcb\xd8\xcf\xf2\xfa\xeb\xf6\x59\x96\xe4\x2d\x03\x21\x9c\xa1\x8c\x20\x1b\x6e\x5d\xd4\xbf\xb9\x4d\xfc\x88\x44\x3f\x4f\xfc\x48\xb0\x6c\x49\xd9\x0c\x92\xc0\xf5\x21\xde\x7f\xda\x19\x22\x6d\x09\x50\x53\xba\x46\xab\x4e\x5d\x67\x68\x36\xae\x03\x41\x1c\x4b\x35\x5b\x79\x5e\x7c\xb8\xba\xa5\x8d\x80\x65\x80\xb8\x64\xff\x3e\xff\x76\xbe\x9b\xde\x59\x4c\x7c\x9c\xaf\xaf\xf7\x62\x78\x8b\x76\xba\x7f\x8b\x1f\x22\x54\x18\xd7\xc3\xbf\xf9\xbe\x3b\x4d\xb0\x72\x11\x64\x99\xa0\x57\xfc\xd8\xad\x70\x70\x86\x03\x8f\x8b\xff\x10\xbd\xf2\xb6\x2c\x40\xfb\xb9\xeb\x57\xf9\xf6\xe5\xee\x28\xad\xa6\x28\x11\xd6\x34\x2f\xfe\x46\x53\x86\x35\xc5\xab\x6b\xe3\x77\x9f\x3f\x01\x00\x00\xff\xff\x94\x2a\xa2\xce\xdf\x05\x00\x00") - -func webdataIndexHtmlBytes() ([]byte, error) { - return bindataRead( - _webdataIndexHtml, - "webdata/index.html", - ) -} - -func webdataIndexHtml() (*asset, error) { - bytes, err := webdataIndexHtmlBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "webdata/index.html", size: 1503, mode: os.FileMode(436), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _webdataMainCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x56\x7f\x6f\xa3\x38\x10\xfd\x3f\x9f\x62\xd4\xea\xa4\x56\xaa\x73\xf4\xd7\x69\xe5\x95\x56\xfb\x55\x0c\x1e\x88\x2f\xc6\x83\x6c\x53\x92\x9e\xfa\xdd\x4f\x60\x03\x06\x42\x57\x95\xa2\x32\x13\x8f\xdf\x9b\x79\xf3\xc8\xef\x92\x8c\x67\xa5\x28\x10\xfe\x3b\x00\x00\xc4\xe7\x5a\xe9\x2b\x87\x3b\x55\x90\xb9\xfb\x39\x24\x9c\x2d\x38\xb4\x56\x3f\x0c\xc1\x63\x47\x65\x79\xf7\x08\x25\xd9\x5a\xf8\x87\xbb\xf0\xf8\xf3\xf0\x75\x38\x1c\x4f\x4a\x4a\x34\xb1\x9e\x54\xae\xd1\xe2\xca\xc1\x90\xc1\x21\x7f\xf2\xb5\xbe\x75\x99\x13\xc6\x31\x87\x56\x95\xe1\x42\x56\xd3\x27\x6b\x1d\x5a\xe6\x50\x63\xe1\xc7\x12\x43\xae\xc3\xfc\xac\xfc\x6e\xba\x76\x7b\xa9\x9d\x70\x2e\x8a\x73\x65\xa9\x35\x92\x15\xa4\xc9\x72\xb8\x7f\xcb\xfa\xbf\x90\x8e\xb1\xee\xa4\xfc\xf6\x80\xaa\x45\x85\xa1\x37\x1e\x2f\xc7\xc6\x54\x8f\x13\xd1\xa7\x43\x4e\xf2\x1a\x3e\xe1\x17\x48\xf5\x11\xa9\x77\x4a\xfa\x13\x87\xe7\x2c\xfb\x2b\x14\x3c\xa1\xaa\x4e\x3e\x8d\xd4\xc2\x56\xca\x70\xc8\x86\x6a\x9b\x0a\x0d\x39\xe5\x15\x19\x0e\x16\xb5\xf0\xea\x23\x22\x9b\x3a\x5e\x6a\xbc\x84\x50\xff\x1f\x93\xca\x62\x11\x0e\x14\xa4\xdb\xda\x84\xdc\xbf\xad\xf3\xaa\xbc\xb2\x82\x8c\x47\xe3\x39\x14\x68\x3c\xda\xc8\x92\x2e\xcc\xa9\x4f\x65\x2a\x0e\x39\x59\x89\x96\xe5\x74\x19\xf0\x34\x11\x46\x82\x72\x1a\xa9\x53\x9f\xc8\xe1\xc5\x62\x1d\x82\x1d\x59\xc9\x3a\x2b\x1a\x0e\xb9\x45\x71\x66\x7d\x20\xa4\x3c\x5e\x3c\x13\x5a\x55\x66\xbe\xb9\x67\xdb\x7a\x4f\xa3\x86\x1a\x21\xe5\x00\x21\xdb\x1b\x96\xb7\xc2\xb8\x46\x58\x34\x7e\x04\xde\x83\xe5\xf0\xdc\x5c\xc0\x91\x56\x12\x72\x2d\x8a\xf3\xaa\xd5\xc7\xf7\x09\x61\x18\xc7\xcb\xf8\x1c\xc9\x5a\x21\x55\xeb\x38\xbc\x34\x97\x6f\xe8\x6d\xd5\xb1\xbb\x45\xf4\x81\xb6\xd4\xd4\x71\x08\x5b\x92\x90\xe5\x25\x15\xad\x8b\x94\xa9\xf5\x5a\x19\x4c\xd6\xe6\x9e\x1a\x34\xca\x54\x4f\x87\xfb\x42\x93\x43\xb9\x69\x0e\x64\xc7\xf7\x01\x53\xf2\x6d\x30\xe4\x0a\xab\x1a\x0f\xcd\x7c\x70\x39\x3a\x96\x93\xf7\x54\x73\x78\x1e\xcf\x5a\xd4\x24\xe4\x62\x04\x09\xef\xb7\x89\xf7\x34\x7a\x10\xad\xa7\x31\x76\x61\x0b\x6d\xf7\x05\x1b\x21\xb5\xc8\x71\x5c\xfb\xd8\xaf\xca\x8a\x6b\x22\x02\x77\x12\x92\x3a\x3e\x04\x86\x25\xee\x67\x37\x7c\x64\x61\x78\x4f\x53\x6a\x3f\x33\x44\x77\xcf\x24\x89\x38\x8e\x46\x14\xca\x5f\x39\x64\xc7\xec\x47\x32\xba\x2e\x2a\x24\x27\x2d\x37\x83\x7f\x1b\x9b\xfc\xbb\x46\xa9\x04\x3c\x24\x9c\x5f\x7b\x55\x3c\x46\x9e\x6b\xde\xab\x3a\xcf\xaf\x1f\x5d\x28\xfe\x35\x76\x09\x7e\xc1\x52\xf9\xd3\x8e\x8b\xdc\x91\x6e\x47\x7d\x25\x37\xbe\xcf\x76\x71\x61\xa3\xb2\xdf\xc7\xce\x97\xad\xd6\xae\xb0\x88\x66\x51\x57\x63\xe9\xe7\x75\x8a\xf3\xcf\x16\xd2\x1f\xa3\x89\x47\x86\x44\x38\x3b\xeb\xf2\x8c\xd7\x9c\x84\x5d\xea\xc5\x06\x1c\xdf\xde\x10\xbf\xb3\xb9\x60\x79\x73\xbc\xc2\xed\xe3\xf7\xd4\xac\x4b\x0f\xa1\x3f\x21\xef\x55\x27\x2c\x8a\x51\xe2\xbd\x49\x56\xb6\xdf\xcd\xe7\xa5\x89\xcc\x85\x2c\x86\xd1\xcd\x91\x9b\x86\xb0\x35\xa8\xc4\x1b\x62\x24\x51\xe1\xca\x42\x77\x36\xda\xa1\x59\xf6\xf8\x4f\x83\xda\xeb\x6f\xda\x86\xc4\xfb\xe6\x1d\x5e\xf9\x63\xaa\x74\x65\x98\x70\x0d\x16\x9e\x59\xe1\x15\x71\x78\xfb\xfb\x75\x92\xfb\xa8\x84\x54\xee\xab\x37\x8f\xa5\x49\xf2\xe1\xcc\x86\xd5\xfe\x14\x37\x14\x6e\x9a\xfb\x2d\x37\x4f\x49\xcd\x34\xbf\x26\x6d\x6d\xac\x34\xb6\x3d\x09\xb2\x95\x86\x37\xaf\xcd\x81\x29\x1a\x39\x2b\x16\x8e\x81\x98\x5b\xff\x14\x9a\x5f\xcc\xdf\xbe\x7c\x57\xf6\x9c\x82\x1a\x2e\x73\x27\xab\xcc\x79\x52\xeb\x48\xf0\x9f\xd9\x9f\x95\x99\x1c\x21\xbb\x85\x6b\x74\x1b\x6e\xc8\x3f\x70\x2d\x9c\x67\x54\x32\x7f\x6d\xf0\x71\xa9\xb1\x85\x94\xf6\xeb\xa4\x9b\xb4\x83\x6e\xfe\x79\x13\x87\xf4\x63\x01\x37\x06\x07\xb4\xff\x07\x00\x00\xff\xff\xc6\x17\xeb\x53\x9c\x0a\x00\x00") - -func webdataMainCssBytes() ([]byte, error) { - return bindataRead( - _webdataMainCss, - "webdata/main.css", - ) -} - -func webdataMainCss() (*asset, error) { - bytes, err := webdataMainCssBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "webdata/main.css", size: 2716, mode: os.FileMode(436), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _webdataSha256Js = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x5b\x77\xdb\x38\x92\x7e\xd7\xaf\xa0\x75\xb6\xd5\x84\x55\x96\x01\x10\xe0\x45\x14\x94\x63\x3b\xce\xa5\x3b\x9d\xce\xe4\xb2\xdb\x33\x1a\xe5\x1c\x90\x04\x49\x8d\x28\x4a\xa6\x28\x3b\x8e\xe5\xfd\xed\x7b\x40\x52\x37\xdb\x99\xf4\x65\x1e\xf6\x85\x02\xc0\x62\xa1\x50\xf5\xe1\x43\x15\xa9\xd3\xe3\x96\x71\x66\xfc\x24\xaf\xe5\x87\xb0\x98\x2c\x4a\x63\x32\x5b\x64\x6a\xa6\xf2\x52\x96\x93\x79\x6e\xcc\x63\xa3\x4c\x95\xf1\xe1\xd5\x99\x11\xcb\xd9\x24\xbb\xd5\x23\xa9\x5c\xa6\x6a\x09\x86\x5c\xb6\x8c\x48\xc5\x93\x5c\x45\xc6\x24\x37\x5e\xbc\x7e\xf7\xc1\x78\xf7\xe9\xdc\x20\x2e\x3e\x61\x86\xcc\xa3\xdd\x10\xc5\x54\xcb\x1b\x37\x2a\xcb\xf4\xaf\x56\x1a\xce\x8b\x42\x2d\x17\xf3\x3c\x9a\xe4\x49\xcb\x78\xf5\xcb\xd9\xc5\xc3\xe9\xe5\xf2\xe9\x09\x3c\x57\xb6\x5a\xc6\xc5\x7c\x71\x5b\x4c\x92\xb4\x34\x28\xc6\xee\x09\xc5\xc4\x35\xce\x8b\x89\xcc\x8d\x8f\xab\x42\x4d\xc1\x20\x9e\xa7\x87\xb1\x67\xbc\x93\xab\xcc\xf8\x69\x9e\xe6\xcb\x72\x9e\x1b\x1d\xe3\x62\x9e\x97\xc5\x24\x58\x95\xf3\x62\xd9\x32\x9e\x4f\x96\x75\x4f\x45\xc6\x2a\x8f\x54\x51\xd9\x77\xfe\xe1\xb9\xf1\x66\x12\xaa\x7c\xa9\x5a\xc6\x07\xa5\x8c\xb4\x2c\x17\xfd\xd3\xd3\x50\x66\x93\x44\xdb\xd7\x4b\x26\x65\xba\x0a\x7a\xe1\x7c\x76\xfa\xaf\xe5\x87\x57\x67\xa7\x46\x3c\x2f\x8c\xd9\xbc\x50\xc6\x24\x8f\xe7\xc5\xac\x5a\x45\xeb\xf8\xb4\xf5\xe3\x6a\xa9\x0c\x3d\x49\x58\xfe\xe8\x9b\xf1\x2a\x0f\xf5\x1d\xf3\x35\xba\xdb\xb4\x8d\x1b\x33\x04\x09\x11\xba\xbb\x96\x85\x91\x09\x0c\x81\x18\x8d\x21\x11\x18\x62\xc8\x61\x0a\x0a\x52\xb8\x82\x5b\x58\xc0\x4c\x1c\x11\x28\xf5\xdd\x42\x5f\x56\xf0\x55\x1c\x11\x3f\x12\xd1\x7a\x7d\x77\xef\xc7\x22\xea\xa9\x3c\x9c\x6b\xaf\xae\xd7\xed\x4f\x1f\x5f\xb8\x6d\x7f\x25\xa2\x5e\xbe\x9a\xbd\x9f\xaf\xf2\x68\xb9\x5e\x13\x7f\x12\x9b\xab\x23\x21\x16\xb2\x58\xaa\xd7\x79\x69\xae\x80\x60\xb4\x5e\x93\xe1\x0a\x95\x69\x31\xbf\x31\x2e\x8b\x62\x5e\x98\xed\xed\x43\xc6\x6c\xb5\x2c\x0d\x69\x4c\xf2\x52\x25\xaa\x30\x86\xc2\x20\x6d\xa4\xf5\x60\x21\x44\xd8\xcb\xe4\xb2\x7c\x9d\x47\xea\xcb\xaf\xb1\xd9\xfe\xf0\xea\xec\xa4\x0d\x18\xa1\x49\x6c\x5e\x89\xed\x72\x03\x90\xe8\xae\x50\xe5\xaa\xc8\x8d\x33\xdd\x83\x10\xdd\xc3\xed\x81\x00\x64\x10\xd7\x3e\x48\x40\x69\xf5\x95\x32\x4a\x59\x5b\x4f\xb3\x5e\xd7\x5d\x6e\x57\x5d\x94\x08\x53\x76\x6d\x3e\x1c\x0e\xbd\xc1\x80\xa1\x2e\xe1\xa0\x04\xb1\x7d\x95\x2d\x95\x71\xb0\x90\x4f\xb9\xfa\xb2\x50\xa1\x0e\xb1\xd2\x23\x1a\x50\x95\xaa\x07\x98\x6b\x23\x3f\x9e\x17\xa6\x1f\xf4\x32\x95\x27\x65\x3a\x10\x89\x8f\x82\xde\x62\xb5\x4c\x4d\x8c\xfc\x60\x24\x87\xc3\x21\x1f\xaf\x05\xa1\xee\x60\x40\xd9\x89\xfc\xc1\xa2\xbe\x14\xb2\x9b\xf9\xc1\x28\x19\x0b\xd9\x61\xd4\x63\x9e\xed\x50\x8f\xfb\xad\x60\x94\x9c\x90\xb1\x90\xa7\xdb\x41\x7b\x8d\xfd\x4c\x6c\xd4\x57\x93\x49\x81\x7d\x39\xc8\x7c\xd9\x15\x0a\xc5\xe2\xcc\x0c\x7a\xcb\x6c\x12\x2a\x53\x82\xec\x2a\x04\x31\x84\xe8\x91\x2b\x50\x20\x46\xf1\x08\x8f\x21\x1e\x11\x7d\xa1\xfa\x62\xe9\x0b\xd3\x17\xae\x2f\xf6\x78\x5c\xbb\x62\xfb\xf0\xc6\x71\x81\x88\xff\x9a\x93\x9a\x30\x06\xf7\xb0\xd8\x8b\xdf\x36\xbc\x9b\x15\xa0\x7b\x38\xb4\x3a\x15\x9c\x50\x50\x82\x52\xf6\x0d\xd3\xb6\x12\xfc\xa9\x38\x5e\xa4\xf3\xa5\xaa\xac\x32\xae\xa5\xde\xec\xa5\x31\x59\x1a\xf9\xbc\x34\x96\xab\xc5\x62\x5e\x94\x2a\x6a\xa3\x3f\xfd\xdc\x54\x9c\x9b\x12\x62\xe4\xe7\xe2\x8b\x19\x22\xbf\x4c\x27\xcb\xde\x52\x95\x9a\x9c\x7e\x56\x0f\x90\x9a\xd4\x38\xad\x50\x7a\xa4\x77\xc1\xec\x70\xef\x54\x8c\x36\x55\xb7\x86\xcc\x0a\x25\xa3\x5b\x63\xa9\xca\x36\xf2\x5b\x1b\xf1\xaf\x87\xe2\x17\x32\xaf\xcc\x51\xa5\xb1\x7b\x32\x2e\x55\x61\x84\x32\xcb\x26\x79\x62\xac\x16\x91\x2c\x95\x46\xa8\x30\x13\xbd\xd9\xd1\xe3\x9d\x2e\x9b\x15\x98\x01\xf2\x03\x21\x7b\xc1\x24\x7f\xa3\x72\x0d\xd0\xde\xb5\xcc\x56\xca\x57\x22\x1d\x0e\x87\x96\x9f\x08\x75\xca\x4e\x2a\x26\x50\x83\xe0\xd4\x45\x77\x35\x14\x6f\x4d\x09\x01\x60\xd0\xeb\x47\xbe\xdc\xdf\x06\x72\xbb\x0d\xe4\x28\x19\x77\x44\x83\x69\xcc\xf0\xfd\x26\x94\x6a\xb8\x55\xf5\xbb\x9f\xd5\xc2\x81\xc0\x7e\xa0\x05\x83\xae\x20\xa8\x1c\x05\x63\x21\x47\xc1\xf8\xb3\x87\x3d\x4e\x29\x73\x6d\x28\xb6\x63\x84\x33\x8f\x73\xdb\xa5\xae\x9f\x8b\x2b\xb3\x84\x1c\xf9\x99\x48\xfd\x99\x38\xc2\xf7\x75\xc8\x6a\x4f\xed\xc2\x25\xeb\x50\x85\x10\x83\x82\x48\x60\x58\x54\x5e\xe0\x7e\x28\xa6\xd5\x7a\x13\xe4\x4b\x11\x6e\xbc\x15\x8b\xb0\xf1\x56\x28\xaa\xfd\x5e\x6d\x53\x25\xb0\xaf\x06\xa1\xaf\xba\x62\x81\xa2\x6e\x3a\x10\xb2\xd3\x31\xb5\x09\x71\x83\x76\x05\xaa\xbb\x40\x90\x23\x88\xba\x22\x45\x7e\xd6\x15\x91\x1f\x88\xcd\xed\x68\x38\x1c\xb6\x38\xf2\x13\x21\x7f\x48\xfd\xaf\x3b\x73\x13\x55\xbe\x92\xcb\x74\xcf\xde\x0d\x09\x46\x90\xc2\x14\xae\xbe\x05\xb1\x06\x33\x1a\x20\x46\xa3\xa4\xc1\xcc\x52\x95\xa5\xc6\xcc\x06\x4a\x15\xb8\x2f\xcc\x18\xf9\xcb\x9b\x49\x19\xa6\xda\x25\xa1\x5c\x2a\xa3\xfd\xea\xf2\xb7\x76\x3f\x3a\xf0\x55\xb3\x89\x9f\x9b\x12\x14\x4c\xd1\xbd\x1f\x14\x4a\x4e\xfd\x5a\xfe\xdc\x66\xdf\x90\xbf\x7c\x5a\xfe\xef\x1f\x2f\x3f\x7c\xe3\x89\x17\xfa\x89\x07\xf2\x67\xef\xdf\x9f\xfd\xfd\xfc\xd3\x8b\x17\x97\xef\xdb\xfd\xb2\xb8\xbd\x4b\x45\xae\x6e\x8c\xb3\xa2\x90\xb7\xe7\xab\x38\x56\x85\x89\xd1\x7d\x28\xf5\x22\xae\xd1\xdd\x81\x3f\xf6\x9e\x3d\xdc\xdb\x46\x70\x6b\x68\x57\x1b\x2a\xbf\x9e\x14\xf3\x5c\x53\x59\x1b\xf9\xf7\x4f\x5a\xf5\xf2\xc0\xaa\x48\xc5\x72\x95\x95\xfd\x83\x89\xea\xd3\xbd\x3e\x14\x03\x65\xbc\xba\xfc\x0d\x8c\x73\x9b\x81\x51\xad\x16\x8c\x79\x61\xec\xaf\x03\xf9\xad\xfb\x2b\x71\xbb\x25\x76\x04\x09\x64\xb0\x30\x73\x54\x1f\x37\xa9\x20\x7e\x3a\x58\xf9\xa9\x46\xbf\x16\xbc\x02\xb5\xdd\x83\x8d\x55\x91\x79\x85\xf6\xf0\xf2\xcb\xd9\xc5\x93\x78\x99\x42\x09\xab\x0a\x2f\xe4\xfb\x78\xd1\xe0\xb8\x99\x94\xe9\x7c\x55\x1a\xf1\xa4\x58\x96\x4f\xe1\xa6\xfc\x73\xb8\x29\xff\x20\x6e\x1e\xc9\xff\x55\xdc\x44\xff\x0f\x71\x33\x5f\x95\x8b\x55\xf9\xe2\x8f\xa3\x67\xfa\x0d\xf4\xac\xc4\x95\x59\x34\x40\x59\x89\x5b\xb3\x4e\x14\x57\x7b\xa8\x59\xa1\xfb\xfb\x6d\x96\x39\x33\xd1\xdd\xae\xf7\xfc\x30\xe7\x6c\xb7\x7d\x79\x2a\x5c\x5f\xf7\x02\x48\xfc\x1d\x31\xcb\x9a\x97\x13\x11\x8e\x82\xe1\x70\x48\xc7\xc3\xe1\xd0\x3d\x36\xad\x6e\xf0\x03\x3b\x3e\x21\x08\xb2\xae\x68\x63\x42\x2d\xc6\x6d\xc7\xf5\x64\x10\x46\x2a\x6e\xf7\xc2\x54\x16\x67\xa5\x99\x0c\x87\x43\xd6\x21\x1c\x75\xff\x8d\x8c\xbe\xbf\x35\xba\x57\xfb\xe9\xd3\x62\xa1\x8a\x67\x59\xaf\x9c\x57\xad\x0b\xb9\x54\x26\xea\x67\xbb\x05\x5c\x3e\x5c\x00\x04\x42\x9e\xba\x90\xe8\xa4\xb9\xb2\x3f\x11\xd8\x4f\x06\x81\x9f\x74\x85\x85\xf4\x40\x2c\x92\x2e\x19\x04\xcf\xc2\x51\xd2\x25\xd5\x5a\xfa\x18\x72\x91\x74\x69\x33\x48\x77\x83\x66\x38\x4a\x0e\x96\x9b\xd4\xcb\xed\x50\xce\xd1\x60\x40\xec\xb5\x19\x6f\x6e\x99\x49\x97\xa0\x83\xdb\xee\x3a\xdf\xbb\x49\xf7\x6e\x42\x2c\xb0\xcf\x86\xb1\x1f\x6b\xaf\xba\xc7\x49\xd7\x3e\x8e\x07\x42\x3e\xd3\x6e\x3c\x3b\xbf\x78\x7e\xf9\xe2\xe5\xab\xd7\x3f\xfd\xfc\xe6\x97\xb7\xbf\xbe\xfb\xdb\xfb\x0f\x1f\x3f\xfd\xf7\xff\xfc\xf6\xf7\x7f\xd4\x2e\x4b\xd2\xc9\xbf\xa6\xd9\x2c\x9f\x2f\xae\x8a\x65\xb9\xba\xbe\xf9\x72\xfb\x75\xe7\xd6\xee\xe9\xd6\xa5\x7a\xf6\x96\x7d\x6c\x5a\x27\x31\xea\xd8\x16\xea\xeb\xe3\xa8\x17\xd8\xec\x9d\x8c\x36\x9e\xde\xf3\xe5\x0b\xed\xcb\x86\x45\xb4\x27\xb3\xca\x93\x87\x38\xc8\xbe\x83\x83\x6a\x71\x51\x57\x7c\x28\x8b\x49\x9e\xf4\xe2\x62\x3e\xbb\x48\x65\x71\x31\x8f\x94\x99\xec\xc2\xbb\x9b\xf4\xe5\xfe\xa4\x7a\xbe\x0c\x82\x47\xdb\x36\x42\x90\xf8\x49\x35\xfc\x69\x92\x97\x6e\x75\x4f\x27\x38\xda\xb0\x4c\x60\x3f\x1b\x44\xfa\xac\x25\x28\x19\x65\x63\x11\x8e\xb2\x03\xdb\xb2\x9d\x6d\xbb\xb4\x75\x6b\xc1\x85\x19\xd6\xf3\x4b\x71\xb7\x87\xba\xfe\x11\x81\xda\x55\xfd\xb6\x68\xc3\x32\x95\x53\xf5\x46\xe5\xfd\x13\x72\xef\x87\xba\x10\xb9\xbb\xf7\xe5\x3e\x4c\x45\xb8\xdf\x5b\xaf\x8f\x88\x7f\x54\x97\x46\xa9\x5c\xfe\x7a\x93\xbf\x2b\xe6\x0b\x55\x94\xb7\x66\xbb\x56\xdb\x46\x9d\x8e\x29\x9b\x70\xe8\xd4\xa3\x6a\xd4\x79\x7e\x30\x9f\x67\x4a\xe6\xed\x23\x21\xca\xdb\x85\x9a\xc7\xc6\xc1\x54\x87\x9c\xfe\x3a\xbf\x96\xd9\x24\x32\xf6\x04\x8c\xfa\x78\xaa\x88\x7c\xbe\x68\x32\x76\x9d\x77\xb6\x97\x55\x60\x0e\x14\x37\x13\x3f\xa9\xb3\xbe\xf7\xa4\xba\xc6\x93\x72\xe7\xc9\xf3\xbd\x58\x3e\x3a\x2d\xaa\xec\xb4\xbf\x6d\x13\xfb\xfc\xf2\xa0\xfb\xe6\xb2\xdd\xff\x37\xbc\xb9\xc9\x73\xb7\x9c\xa9\xf5\x81\xd1\x68\xaa\x18\x73\xa3\x06\xf9\xf7\xcd\xe4\xe1\x37\x8f\x2a\x08\xa0\x89\x7a\x2c\x36\x69\x2a\x44\x9b\x2a\xbb\xaa\x6a\x8f\x84\x88\x7f\xa0\x87\x6e\xa9\x61\x6d\xcc\x63\x4d\xd9\x86\xf6\xe0\xd6\x9e\x49\x6e\x04\xb7\xa5\xfe\x0d\x8b\xaa\x50\x5a\xb6\x75\x02\x1e\xac\xd7\x23\x3c\xae\x11\x83\xfd\x2b\x11\x56\xb9\xb7\xc6\x6d\x24\xb0\x1f\x0d\x62\x3f\xea\x0a\x8a\xee\xa6\xbb\x52\x5c\xf6\x96\xab\x60\x59\x16\x66\x04\x14\x01\xb1\x2b\x48\x4c\x96\x6f\xe5\x5b\x73\x8a\xbe\x6b\x4f\x38\xcf\x4b\x39\xc9\x97\xc6\xa4\x09\xa1\x26\x03\x19\x96\xaa\xd0\x06\xb5\x52\x51\x65\xa1\x04\x75\xaf\x9a\x9c\x56\xe7\xc1\x74\xaf\xdc\x55\x87\xe5\xae\x1a\xaf\xc5\x74\x30\xa8\x36\x52\x5a\x6f\xa4\xfb\x3a\xf4\x77\x55\x86\xdc\x0f\xa0\x4e\x9a\xfb\xec\x38\xee\x86\xf7\x87\x67\xf2\xc7\xcb\xdf\x3e\x1e\x78\x3e\x84\x60\x43\xd7\xd5\x6b\x0d\x81\x9b\x17\x1b\x33\x58\x40\xb1\x73\x58\xf5\x36\x43\x3b\x2c\xaa\x1c\xa6\xa1\x5b\x21\x48\x08\x21\x2b\x1a\x2f\x84\x05\x4d\x42\xbe\x29\xa5\x95\x66\x80\x9a\xe2\xc3\x8a\x03\x35\xed\x9c\x95\xa6\x42\x90\x8b\xd1\x18\x08\x75\x87\xf1\xb3\xbc\x5e\x5c\x8c\xfa\x14\x33\x3d\x60\x36\x23\xc4\xa3\x6b\xcd\xe6\x36\x82\xcd\x08\x75\xd7\xb1\x66\x4e\xd4\xe7\x9c\x7a\xf6\x30\x5e\xaf\xb9\x63\x31\x36\x10\x5b\x3d\x94\xb2\xea\x29\x42\xa1\x12\xd7\x0a\x3a\xb6\x05\xdb\x67\xfb\xa6\xb6\x0b\x62\x61\x73\x6e\xd9\x5d\xd3\x8c\x3b\x04\x53\x4b\x1f\x1f\x78\xfd\xc0\xce\xfa\xce\x66\x7a\xca\x70\xad\xda\xdd\xaa\x26\x74\xab\xfb\xd1\x44\x08\x52\x81\xfd\x74\x90\x6f\xfc\x51\x25\x93\x77\x0b\x31\xed\xb6\xea\x60\xcf\xc4\xe2\x41\xb0\x67\x87\xc1\x9e\x8d\xd7\x22\x1f\xa5\xe3\x2a\xde\x45\x77\x51\xc7\xdb\x9f\x76\x05\xd9\x56\x78\xdb\xdd\xab\x43\x51\x97\x9d\xd5\xb6\xdb\x8b\x0c\x85\x5c\x1c\x8c\x77\x3a\x47\x78\x4f\xf4\xa8\x1e\x22\x4f\x06\xf0\xee\x51\xf4\x6a\x1e\xcd\x3b\x1d\x33\x15\x71\x73\x7c\xa6\xfa\x78\xad\xce\x5e\xe4\xeb\x15\xfe\x81\x05\xc6\x8f\x57\x47\xbf\x01\x69\xf7\x78\xda\x8d\xee\xbf\x93\xc6\xee\x73\x09\xde\x91\x88\x46\xb4\xc6\xed\x89\x4e\xc3\x65\x6f\xa9\x64\x11\xa6\xe6\xe9\xe7\x91\x3c\xf9\x7a\x76\xf2\x0f\x7c\xe2\x89\xee\x3f\x4f\xc7\xdd\xff\x3a\x45\x4f\x73\xef\x76\xe3\x56\xdc\x22\x97\xea\xc4\x66\x46\xc3\xdf\xba\x98\x93\xbd\xc9\xe6\x7d\x9b\x68\xa3\xaa\xc2\x2f\xd4\x22\x93\xa1\x32\x4f\xff\x29\x4e\x13\x68\xb5\xeb\x17\x74\x27\xe4\x48\x88\x69\xa7\x33\x1d\x6c\xa8\xee\xe9\x09\x7f\x14\x3f\x1a\xf1\x7c\x95\x47\x4f\x4e\xf8\x90\xcb\x66\x7b\x5c\x36\x15\xd8\xdf\x69\xd7\x1e\x65\xe8\xee\x4a\x6c\x99\x6c\x0a\x0c\x6d\xe8\xa6\x0a\xf9\xd5\x41\xc8\xa3\xff\x44\xd2\xb3\x71\xc6\xd5\x48\x8d\x11\xa4\x6b\x11\x0d\x06\xc4\x3d\xb1\x8f\xd5\x5e\xf1\xbe\x99\xf7\x84\x34\x60\x5b\x88\xb8\x3b\x6b\xf8\xf8\x21\x76\xa2\x43\xec\x44\xe3\xb5\x30\x35\x5b\x12\xfb\xc4\x3d\x56\x9b\x24\x4f\x13\xe3\x06\x4a\x3a\x9d\xbb\xff\x26\x96\x1e\xd3\xe3\x13\x25\xce\x0e\x4d\xd1\xe6\xb5\xef\x23\xd7\x4f\xf7\x5c\x9f\x0b\xec\xe7\x3b\xd7\xe7\x5d\xd1\xd2\x0e\x95\xfb\x5b\x28\xd7\xd4\x90\x77\xa7\xd0\xb0\x3d\xec\xb1\x7d\xa7\xb3\x5d\x23\xd4\x6c\x1f\x1d\xb2\xbd\xff\xad\xe5\x6c\xe6\x7c\xb4\xaa\x3f\x52\x88\x65\xff\xc9\x42\xec\xf7\xf8\x2e\x6f\x7c\x97\x3e\x4c\x27\x25\xda\x3b\x96\x65\x4f\x1f\xe6\x6f\x6a\x9f\x46\x1a\x29\x4a\x44\x5d\x7d\x5e\xa9\x07\x0e\x9c\x1e\x3a\x70\x3a\x5e\x8b\x74\x14\x8d\x1b\x1f\xaa\xef\xfa\x70\x37\xcf\x9e\x1f\x7f\xf7\x2b\x07\x7d\xbe\x7e\xb7\x74\x7c\x9c\x75\x17\x75\xa6\xd6\xdc\xd0\x0e\x91\xeb\x70\x30\xb0\xe8\xc9\x5e\x3a\xf7\xd3\xa6\xb6\xda\x88\x75\xe4\xe7\xff\x0d\x3b\x7b\x6a\x7e\x7e\x42\x22\xec\x44\x9f\xe5\xbe\xd0\x1b\x9d\x86\x35\x02\x7a\x5e\x8a\x3e\xeb\x1f\x62\xd5\xbf\x94\xa2\x9d\xec\x2f\x0f\x64\xed\x46\x96\x34\xb2\x7c\x4f\xf6\xed\x03\x59\xa7\x91\x75\xd1\xe7\x2a\xc2\x3b\xc9\x5f\x1f\x48\x92\x8d\xa8\x57\x8b\x12\xbc\x93\x7d\xb7\x5f\x8f\x98\x61\x47\x1f\xda\x1c\x75\x4d\xd9\xb4\x9a\x48\x9a\x66\xf5\xa0\xad\xef\x6c\x1a\x51\xdd\x68\x04\xab\xea\x30\xaa\x3b\x3b\xf5\x7f\xab\x3d\x06\x59\x3d\x45\xf0\xd4\x14\x5d\x33\xda\xb6\xb2\xdf\x3d\x6d\xd7\xcc\x36\x0d\x5d\x99\xb5\x1e\x58\x12\x3c\xb4\xe4\xfd\xc6\x12\x08\x9a\xcf\x2d\xbf\xd7\x96\xae\x19\xfc\x59\xab\xaa\x46\xf2\xd8\x4f\xc9\x43\xeb\xbe\xec\x8a\xb2\xd1\x18\xa2\xef\x7d\x66\xda\x14\x1b\x62\x64\x51\xcb\xb5\x1c\x82\x2d\x0a\x1e\x61\x84\x63\xdb\xb6\xc0\x25\xd4\xc1\xd4\xf3\x3c\x60\x84\x31\x8f\x50\xdb\x73\x80\x51\x0f\x3b\x0e\x77\xb9\x03\xc4\xe1\xd8\xc6\x16\xa6\x1c\x88\xed\x31\xec\xd8\xae\xe5\x81\x45\x31\xc3\x0e\x67\xd4\x1d\x43\x24\x46\xc4\x71\x3c\x6c\x59\x0e\xb6\xc0\x22\x8c\x11\x8b\x51\xc7\x01\x82\x89\xe5\x61\x46\x19\x05\xea\x38\x16\x73\xb1\x63\x53\x20\x16\xf7\x5c\xcf\x22\xc4\x03\x6a\x63\xec\x52\xea\x51\x06\x9c\xba\x8e\xc5\x6c\x8b\x03\xe1\x8c\x30\xee\x51\xca\xc7\xb0\x2d\x52\x36\x9f\x49\xfa\xfb\x5c\xba\xf9\x32\xd2\x97\x22\xf2\x1f\xde\xb0\x5c\xa6\x6f\x8c\x34\x93\xcd\xa0\xbe\xb6\xf6\x3b\xdf\xb8\x8e\x1f\x69\xe2\x84\x3e\xd2\xf4\x07\x14\x3d\xc9\x57\x9f\xf2\x69\x3e\xbf\x39\xf8\xe4\xa2\x09\xfb\xf1\xa7\x99\xb7\xf3\x7d\x99\xe5\xc1\x07\x99\xc7\x55\xe6\xd9\xc1\x2b\x1f\x08\xea\x77\x3d\x4d\xd6\x35\x83\x02\x16\x70\x03\x25\x7c\x81\x15\x7c\x85\x33\x38\x87\x0b\x78\x0e\x97\xf0\x02\xae\x35\x94\x5e\x3e\xfc\x8e\x16\x1d\x7e\x52\x8c\x50\x21\x6c\x06\x37\x82\xc0\x0b\xf1\x76\x35\x0b\x54\x01\xa5\x78\x07\x5f\xc4\xdf\x60\x25\xde\xc3\x57\xf1\x16\xce\xc4\xaf\x70\x2e\xde\xc0\x85\xf8\x05\x2e\xc5\xcf\xf0\x5c\xfc\x04\x2f\xc5\xab\xbf\xf6\x3d\x2d\x12\x52\x9f\x52\x99\x90\x23\x32\xf6\x03\x21\x47\x74\xec\x27\x42\x8e\xac\xb1\x1f\x0b\x39\x62\x63\x3f\x17\x72\xc4\xc7\x3a\xf3\x1b\xd9\x63\x5f\x09\x39\x72\xc6\xd5\x99\xb5\x10\x2d\xec\x2f\x06\x85\xbf\xd0\x27\x15\xb1\x87\x8b\x67\x3a\x15\x3e\xbe\x81\x54\x84\xbb\x54\xf8\x19\xee\x87\xa3\xd9\x18\x66\xfb\x83\x5d\x52\x0f\x77\xc9\x18\xae\x47\x8b\x71\x75\x2c\xbe\x30\x53\x98\x21\xd4\xaf\x06\xbe\x98\x67\xe6\xf5\x68\x71\x42\xc7\x48\x4b\x9c\x38\x63\xf8\x5a\x0d\x10\xde\x8c\x10\x5b\x67\x5c\x62\x65\x2a\xb8\x30\x63\x04\xcf\xcd\x2a\x22\x08\x5e\x8e\x16\xb5\x56\x04\x33\x51\x9a\xe7\x66\x84\xe0\xd2\xac\x49\x07\x81\x12\x53\x98\x8a\x1c\x72\x11\x43\x2c\x4a\x33\x81\x14\x41\x22\x02\x08\x44\x06\x99\x88\x20\x12\x65\x65\x88\xaf\x5d\x23\x4a\x33\x02\xdd\xd0\x5d\xa2\xbb\x19\xe8\x86\xee\x52\xdd\x0d\x40\x37\x74\xd7\x1a\x57\xda\x74\x43\x77\x99\xee\xc6\xa0\x1b\xba\xcb\x75\x37\x07\xdd\xd0\x5d\x5b\x77\xa7\xa0\x1b\xba\xeb\xe8\xae\x02\xdd\xd8\x43\x9f\xc6\xda\x2b\xff\x95\x18\x11\x42\x6c\x8b\x53\x86\x5d\x20\xae\xe7\x31\xe6\x30\x46\xc0\xc2\xcc\xb3\xa8\xc5\x1c\x02\x96\x47\x09\xc6\x1e\x77\x2c\xf0\x6c\xe2\xb9\x0e\xd1\x35\x1a\xc7\xae\xe7\x60\xcf\xb3\x80\x32\x6e\xd9\x16\x77\x98\x0b\xd4\x75\xb0\x63\x5b\x94\x12\xb0\x6c\xca\x2c\x97\x60\x17\x83\x45\x30\xf7\x5c\x86\x09\xd8\xd8\xa1\x94\x53\xc7\x05\xc2\xa8\xed\xba\x5a\x1b\x10\x8f\x72\xec\xb8\x96\xeb\x02\x25\x36\xc5\x8e\x4b\xb1\x0d\x2d\x6a\x13\xe6\xba\x2e\xd1\xec\x44\x99\x4b\x29\xe5\x5a\x97\x6b\x71\x4d\x4f\x98\x00\xc3\x94\x52\xca\x1c\x87\x01\xb5\x99\xc5\x1c\xec\xb8\x60\xe3\x9a\xb0\x5c\x70\x1c\x4c\x39\xf7\x5c\x5d\x4f\x32\x8f\x70\x4c\x28\x05\xc2\x39\xc7\x2e\xb1\x3d\x0a\xc4\xf3\x6c\x6c\x33\xcf\xb5\x81\x72\xce\x28\xc5\xae\x4b\x81\xba\x94\xb8\x16\xb3\x98\x07\xd4\xe3\xd4\xf3\x6c\x17\xbb\x60\x51\x82\x2d\x62\xd9\xda\x1b\x96\x65\x73\x87\xb8\x1e\x01\x8b\xbb\x4c\x33\x20\x21\x40\x88\xe5\x51\x5b\x7b\xc3\xb2\x5c\xca\x88\xeb\x71\xb0\x6d\xdb\xc2\x0e\xc5\x1c\x1c\xc7\xd2\xaa\xaa\x82\xda\x63\x0e\x77\x2c\x47\x33\xaa\x67\x13\xcd\xa3\x44\x53\x34\x27\xae\xe5\x60\x0c\xc4\x73\x6d\xdb\x26\x98\x13\xa0\x44\x2f\xc1\xb6\x38\xd6\x2e\xb6\x3d\x6e\x63\xcb\x01\xea\x58\x98\xb9\xdc\xa3\x44\xdb\xaa\x29\x9e\x11\x02\x16\xe5\x9e\x63\x61\x17\x63\xb0\x2c\xc6\x1d\x9b\x39\xda\x56\x4e\x6c\x6c\x73\x97\x38\x60\xd9\x18\x5b\x9c\xba\x98\x01\xc3\x1e\xe3\x0e\xf1\xb0\x07\x54\x1f\x06\x96\xc5\x18\x30\x0b\xd3\x8a\xee\x81\x63\xdb\x63\xae\x4d\x6c\xb0\xb9\x87\x6d\xcc\xb9\x0d\xae\x6b\x79\x9e\xe3\x3a\x0e\x78\xdc\x25\x96\xc7\x1d\x02\xc4\xa2\x54\x47\x45\xd7\xf2\x5c\xdb\x4e\xb1\xc6\x85\xc3\x1c\xd7\xb1\x1c\xc7\x03\xe2\x71\xce\x6d\x1d\x23\xa0\xda\x4a\xcc\x5c\xc2\x81\x56\xd3\x60\xc6\x29\x50\xcb\x26\x2e\xa7\x8c\x32\xa0\x8c\xba\xcc\xb2\x99\xc3\xa0\x45\x1d\x6e\x3b\x16\x23\xae\x53\x1f\x58\x16\x61\x8e\x07\x96\x45\x3d\x8b\x72\xea\xb9\x63\xbf\xbd\xa1\x4e\xcd\x6f\xcd\x5b\xbf\xfa\x5f\x35\x9d\x4e\xfd\xdb\x93\xb3\xe8\x59\xdd\xdc\xfd\x37\x65\x9b\x32\xdd\xdc\xa3\x7e\x7b\x95\x37\x7f\xc4\xd9\x7b\x75\xa8\xbe\x68\x9a\x5e\x3e\x33\x9f\xbc\x3b\x9b\x47\xab\x4c\x75\x3a\xf5\x6f\xaf\x11\xee\x74\xcc\xc3\x01\x71\x83\x60\xd7\xec\xbf\xee\x55\xff\xa4\x11\x37\xf7\xc8\xd4\xe9\x3e\xf2\x5b\xff\x17\x00\x00\xff\xff\x8a\x1d\x7d\x6b\xa4\x24\x00\x00") - -func webdataSha256JsBytes() ([]byte, error) { - return bindataRead( - _webdataSha256Js, - "webdata/sha256.js", - ) -} - -func webdataSha256Js() (*asset, error) { - bytes, err := webdataSha256JsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "webdata/sha256.js", size: 9380, mode: os.FileMode(436), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _webdataTexPng = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x00\x26\x0f\xd9\xf0\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00\x00\x40\x00\x00\x00\x40\x08\x06\x00\x00\x00\xaa\x69\x71\xde\x00\x00\x0e\xed\x49\x44\x41\x54\x78\xda\x75\x9b\xe7\x72\x1b\x4b\x0e\x85\x31\xdd\xd3\x13\x49\x51\xf2\x7d\xff\x47\x5c\x8b\x69\x72\xe8\xfb\x63\x01\xd7\x67\xac\xd6\x55\x2a\xd2\x13\x3a\xa0\x81\x83\x83\xc0\x42\x44\x3e\xe5\xbf\xff\xee\x22\xf2\x25\x22\x2f\x11\x29\x44\xe4\x43\x44\x46\x11\xa9\x45\x64\x12\x91\x56\x44\xa2\x88\x3c\x45\xa4\x14\x91\x55\x44\x92\xde\x2f\x74\x8c\xa7\x8e\x21\x22\xf2\x5b\x44\x6e\x22\xf2\xd6\xf7\x2e\x22\xb2\x88\xc8\xa6\x63\x05\x9d\xf3\x43\xaf\x1d\x22\xd2\x8b\xc8\xa0\x9f\x9b\xce\xc3\xb1\x0b\x11\x69\x74\x8d\xb5\x5e\x5f\x74\x9e\x5d\xc7\x4c\x3a\xd6\x53\x44\x3a\x11\xa9\x44\xe4\xd4\x35\x1c\xf8\xbc\x8b\xc8\x67\x21\x22\xff\xe8\x66\xa2\x2e\xf6\xa2\x0f\xd8\xdf\xa9\x93\xd5\xd8\x98\x88\xc8\x2f\xfd\x3c\xf4\xfd\x56\xbf\x8b\x2e\x30\xeb\xe6\x6c\x61\xab\x2e\x5e\x9c\x40\xef\xba\xe8\x02\xf3\x65\x9d\xaf\xd2\xe7\xa3\x7e\x9e\x2a\x98\xac\xe3\x66\xdd\xe4\x43\x85\x60\x07\x79\xc5\xbb\x6f\x9d\x4b\xdc\x7c\x8d\x88\xcc\x85\x4e\x7c\xd3\x81\x1b\x1d\xb8\xd0\xc1\x0b\x11\x99\xf5\x7a\xc4\x06\xc5\xfd\xdf\x16\xde\x38\x21\x5c\xf5\xfb\xa6\xf7\x3b\x3c\x2f\x38\x11\xd1\xb9\x46\x08\xe6\x70\xc2\x7a\xe9\x18\xbf\x70\xf2\xb5\x6a\x4c\x83\x75\xed\x2a\xec\x52\xd7\xde\xeb\xb8\x9d\x8e\x1b\x39\x77\xd0\x53\x8a\xfa\xe2\x1d\x2a\x57\xe8\xf5\x80\x17\x16\x6c\xee\x50\x21\xd9\xa4\xa6\x7a\x0f\x8c\x11\xf5\x1d\x33\x15\x53\xbd\x51\x37\x33\x61\xe1\xd9\x09\x68\xd0\xef\xad\x6e\xe4\xaa\xe6\xfa\x82\x60\xb8\xf9\xbb\xce\x57\xeb\x38\xa7\x0a\x21\xc2\xcc\x0f\x98\x5b\x14\x91\xdf\x51\x17\xbb\xaa\xa4\x0a\x48\x48\x74\xf3\x41\x17\x57\xea\x46\xde\xaa\x5e\xb6\xb9\x52\x27\xb5\x67\x4d\xf5\x6c\xc3\xb3\x7e\x8f\x3a\x8e\x61\x40\xd2\x3f\x5b\x7c\xa3\x8b\x36\xed\xab\x74\x83\x2d\x4e\xae\xd0\x8d\x47\x7d\xbe\xc4\x5a\x7b\x9d\x6f\xd2\xcf\x46\xff\x82\x0a\xcd\x84\x61\x9a\x91\x04\x17\x2e\x6a\xdb\x3d\x06\x7c\xe9\xc9\x6e\xba\x81\x13\xf6\x65\x42\xb2\x05\x3c\x74\xa1\xa3\x5e\xcb\xba\x88\x0a\x9b\x34\x01\x35\xba\x80\x08\xa1\x98\xe9\x94\x18\xdb\xd6\x76\xa8\xd0\x57\x08\xbb\xd0\xb1\x0c\x28\x03\x80\xb2\xd2\xbf\xd5\x1d\x5e\x80\x69\xd9\xbd\x22\x02\x3c\x3a\x7d\x38\xea\xe6\x3f\x75\xa0\x11\xf6\x66\x92\x0b\xb8\xb6\xeb\xe6\xef\x2a\xc0\x87\x6e\x28\xc1\x56\x03\xd4\x3d\xea\xb8\x19\xf3\x65\x00\xe1\x5d\xc7\x34\x01\x6c\x7a\x40\x83\x2e\xdc\xbc\x52\xa9\xd7\x78\x08\x3d\x4e\xb8\xc6\xf5\xc6\x61\x4f\x65\x42\x8c\x58\x68\xd6\x81\x33\x54\x67\x56\xc1\xd4\x58\xd0\xa4\x2f\xdb\x26\x16\x7d\xde\x54\xbf\xd1\x4d\xd4\x50\xbf\x46\x17\x6f\x9a\xf0\x8d\x93\x7c\xe8\x1c\x87\x9d\x8a\x6a\x9b\x01\x6b\x82\x49\x34\xba\xe9\x4e\x3f\x03\xdc\x9c\xcd\x61\x58\xf2\xad\x02\xa9\x60\xb6\x33\xc6\x7a\x89\xc8\x1e\xa0\x3e\x41\x25\x7f\xc0\xdd\x25\x98\xc4\x81\xd3\xb3\xeb\xab\x9e\xce\x7f\x30\xce\xa4\xae\xd5\x5c\xce\x0d\xa7\xdd\xe9\xc4\xbf\x1c\xfa\x8b\x6e\x68\xd7\xcf\x03\x18\x63\xdc\x21\x38\x20\xeb\x01\x9a\x01\x3c\xa2\x71\x60\x6e\xa6\xfa\x82\x8b\xb7\xef\x75\x04\x8a\x07\xd8\x7a\x05\x9b\x35\xa0\x1b\xb1\xf1\x84\x13\x78\x81\x4b\xd8\xf5\x0a\xbe\x3c\x42\xea\x01\x60\x97\x01\x66\xe2\xfe\x7f\x40\xd5\x2b\x77\x10\x27\xd6\x69\x26\x92\xc0\x63\x6c\xbd\x37\x60\x92\x3d\xb7\xa9\xd0\xcc\x63\x55\x11\xf6\x55\x80\x79\x0d\xba\x20\x5b\xb0\xbd\x6c\x98\x30\xeb\x7b\x11\x13\xbc\xf5\x5d\x53\xed\x05\x4c\x72\xc4\x89\x1b\x43\x9b\x70\xca\x04\x28\xdb\xcc\xa4\x9b\x28\x60\xd3\x77\xa0\x7d\x01\xe0\xe4\x49\x07\xc5\x24\x73\x91\x46\x9a\x2e\xd0\x70\xf3\x68\x6b\xd4\xc1\xff\xd1\x9b\xb6\xc1\x5e\xaf\x9b\xcf\x6c\x71\x8a\x09\xee\x2c\xb9\x01\x47\x80\x8f\x81\x57\x80\x86\x99\x80\x32\x3e\xa9\xc2\x11\xdf\x5b\xb0\xd1\x0a\xe6\xb6\x62\xa3\x01\xa0\x9d\x20\x98\x17\x04\xb9\xab\x20\x7e\xeb\xfb\x46\xb8\x0e\x11\x99\x23\x7c\x7a\x09\x9a\x59\x82\xca\xda\xa2\x9e\x40\x6a\xaa\x5e\xa5\x40\x36\x69\x1c\x30\xea\x84\x6f\x9c\x40\x09\xb7\x45\x5a\x3b\x80\x65\x8a\xbe\x6b\x9a\x76\x60\xb1\x76\x30\xb6\xf9\x13\x6e\x74\x83\xd0\x4b\x70\x90\xa8\xcf\x05\xfd\xbc\x80\x94\xd9\x1c\x7d\x00\xe5\x35\x00\xeb\x75\xb3\x27\x98\x9d\xa8\x5b\x0c\xba\x31\x9e\xf4\xa4\x83\x57\x88\x25\x16\xb8\x57\x5b\x28\x37\x62\xe0\x77\xd1\x77\x66\x9c\x76\xeb\xdc\x66\xd6\x67\xae\xc0\xa4\x84\x53\x4c\xba\xde\x08\x0f\x62\x26\xb7\x02\x9b\x04\x87\x1d\xcc\x6c\x2d\x18\x1a\xc1\x9e\xcc\x95\xb4\xb0\xd1\xda\xf9\xf4\xe0\x82\x1e\x0b\x5c\x9e\xba\xf1\x41\xb5\xe7\x70\xbc\x3e\xc3\x76\x2b\x75\x55\x37\x98\x49\x01\xef\x62\x66\x77\x42\x90\xdc\x68\xed\x22\x3c\x7b\x6f\x03\xd0\x06\x77\x7f\x03\x50\xf7\x46\x84\x6a\xd8\x70\x01\x04\x2e\x15\x74\x2e\x70\x4f\x54\xb7\x59\x37\x59\xc1\x9e\x7b\xbc\x7f\x00\x1f\x02\xe8\xee\x0b\x76\xd9\xc2\x23\x9c\xce\x25\x9b\xea\x2e\x18\xaf\x82\xb6\x74\x10\x6a\x00\x6f\x30\x97\x67\xd4\xbe\xc0\xe6\x03\x40\x32\x88\xc8\x14\x41\x64\x22\x1e\x28\xb1\x11\x23\x44\x2d\xc8\x8e\xc0\xed\xb4\xca\x03\x1a\x6c\xe0\xe1\xdc\xcd\x37\x26\x4d\xc0\x86\x8c\xf1\x36\xb8\xd8\x13\xff\x3f\x61\x7a\x15\x02\x2b\x33\x29\xf3\x50\x2b\x22\xc1\x1e\xe6\x69\xf4\xda\x4e\xfd\xd4\xe7\x5e\x22\x72\x58\xa4\xc4\x10\x78\xd0\x17\x8d\x1a\x5b\x58\x6b\x9c\xbc\x03\xb0\x95\x38\x79\xaa\xdb\x0a\xd5\x4d\xf0\xbf\x46\xab\x3b\xb8\x34\xd2\xe1\x12\x66\xb2\xea\x67\x0b\x97\x59\xaa\x19\xf4\xb0\x6f\x86\xb9\xa5\x0a\xd7\x08\x14\x4d\x38\xc1\x3b\x18\x16\x7d\x44\xd0\x5a\x01\x50\xec\x50\xeb\x13\x12\xbc\x42\x48\x25\xc0\xcc\x10\xdd\x62\x85\x15\x82\x12\xa8\x66\x0b\xac\xf8\x70\x82\x37\xe0\xdb\x41\x86\x12\x68\x6e\x02\xf5\xdd\xb0\xc1\xe8\x98\x2a\xd9\x6a\x09\x52\x26\x78\xdf\xf0\xee\x8f\xe4\x09\x28\x25\x5c\x8d\x00\x41\x1b\xa8\x62\x0f\x97\x46\x54\xed\xf4\xfe\x07\x36\xf5\x40\x14\xf7\x5b\xc7\xe9\x60\xe3\x01\x26\x95\x71\x3a\xd1\xd1\x57\x6a\x67\xab\x63\x12\xa0\x4d\xad\x23\x38\xcd\xe0\x58\xe4\x0a\x93\x9f\x44\xa4\x34\x57\x24\x18\x88\xe4\xe3\xff\x65\x80\xcc\x35\x7d\xba\x28\x4b\x5c\x2e\xa1\x70\xd7\x5e\x50\xd9\x03\x99\x1a\xa6\xbd\x98\xde\xb2\x77\x5a\xbd\xfe\x89\x50\x78\x81\x16\xda\x78\x3b\xbc\xc4\x8a\x54\x98\x99\xcd\x1b\xe1\xb2\x88\x48\x8c\x60\x59\x93\xfe\xd5\x88\xa3\x4d\xc2\x4f\x00\xda\x8c\x81\x0b\xe7\x8f\x0b\x4c\x72\xd7\xe7\xde\xce\xc6\x0b\x10\xa0\xb7\x4b\x9e\x10\x7b\x36\x3c\x9b\x10\x01\x8e\x88\x2c\x67\x08\x61\xd6\x79\xc9\x12\x07\xf0\x89\x5d\x35\x73\x82\x00\xc6\xa8\x76\x5d\x20\xa4\x9d\xa1\x22\x3d\x36\x12\x60\x57\xe1\x87\xcd\xbc\x90\x33\xb0\x88\x50\xc0\xe3\x09\x72\x01\x00\xd5\x23\x91\x31\x63\xde\x1d\x1b\x2a\xc1\xfa\x22\x48\x54\x82\x56\x9d\x08\xd5\x4b\x68\x60\x03\x93\x39\x61\xb2\xab\x88\xf4\x11\x2e\xa5\x41\x52\x81\x8b\xad\x61\xab\xa3\x8b\xed\x4d\x4b\x2a\x00\xce\x84\x34\x75\xc4\x73\xb3\x4b\x4e\xee\x48\xc1\x59\xb4\x79\x45\x7e\xb1\x82\x29\xbd\x21\x04\x03\xb8\x87\x6a\xcd\x0d\xb8\x50\x43\x0b\x99\x29\x9a\xa1\xd9\x34\x83\xb7\x05\x43\x5f\x48\x3e\x04\xd8\xdd\xdb\x05\x40\x09\x83\x32\x17\xb7\x21\x26\xb8\x81\xd7\x07\xdc\xcb\x2e\xc1\x61\xdf\x17\x2c\x28\x42\x5b\xc2\x0f\xae\x30\x80\xe4\x78\x52\xb4\xea\x7a\x6b\xcc\xc7\x58\x63\x80\x86\x1a\x6f\x90\x08\xc6\x74\x47\x14\xf8\x01\x5b\x1f\xe1\x0d\x76\xa8\xd0\x03\xd7\x83\x7b\x66\x05\x2b\xec\x90\x59\x12\x00\xac\x05\x58\x44\xfe\x1a\x3e\x9f\xd9\xa6\x03\x99\xe4\x4f\x68\xd5\x06\x8e\x62\x73\x46\xb0\x42\xcb\x78\x59\x2e\xf3\x89\x9a\x41\x25\x22\x39\x42\xba\x09\xd2\x7e\x02\xa4\x2e\x28\x9c\x04\x70\x80\xfa\x07\x6d\x60\xf8\x1c\x91\x27\x68\x70\xd2\x11\x29\xb1\x1a\xb1\xbb\xc0\x8e\x5b\x44\xa6\x15\xee\x5d\x91\x9e\x8f\x00\xe3\x0c\xe1\x0d\x3f\x30\xdb\x0c\x3c\x58\xa0\x65\x4b\x00\xc1\x99\x61\xb3\x01\x19\xa0\x19\x92\xa5\xdb\x9a\x20\x80\x37\x48\x54\xe9\xdc\x29\xdd\xac\x05\x2b\x9f\x18\x83\x2a\x5c\xe9\xf3\x86\x35\x3d\x38\xc0\x0a\x77\xbb\x81\x27\x30\x65\xd7\x20\x0f\xb0\xb8\x02\x8a\xcd\x37\x83\x7d\x1e\x41\x27\xb4\x45\xd9\x64\x17\x9d\x9c\x2a\xb8\x62\xf3\x0f\x80\xd2\x08\x36\x97\x9d\x40\x5e\x8e\x3b\x64\x10\x93\x11\x3e\x9d\x5e\xa3\x44\x91\x66\x45\x76\x89\x87\xf3\xa1\x5a\x7a\x60\xbe\x07\x34\xc5\x12\xb1\x0c\xdb\x23\x68\x78\xe1\x53\x62\x2c\x2b\x31\x32\xeb\x10\xc9\x31\x4a\xeb\x81\xe2\x8d\x63\x90\x25\xd4\x30\x80\x2d\x9e\x2e\x0b\x64\x41\xcd\x03\x14\x75\x46\x4c\x21\xa8\x21\x6c\x60\xab\x77\x1c\xcc\x05\xf7\x78\x40\xc9\x21\xfe\xa9\xdf\x6f\xa0\xfa\x87\x85\xc3\x33\x22\xa4\x06\x3e\xfe\x1b\x68\xda\xc0\xd7\x33\xb9\x90\x91\xc5\xcd\xf0\xcb\x57\x94\xaa\x4a\x64\x62\x1b\x14\x4d\x32\x4c\xc3\x5c\xb0\xd1\xd7\x1b\x5c\x65\xe7\xc2\x6a\x41\xe2\xd6\xb4\x31\x3b\xfe\x6f\x02\x3f\x11\x34\x7d\x41\x0b\x2d\x4c\x5e\xa2\xaa\x5e\x05\xd5\x0b\x88\xf0\xb2\xa3\x9a\x9b\x8b\x1b\x04\x88\xba\x20\x3b\x14\x11\x18\x95\x70\x85\x56\x07\x48\xd0\x86\xa7\x82\x99\xa9\xec\x27\xd8\xde\x82\xf7\x22\x00\xb6\x81\x20\x04\x07\xe8\xf3\x95\x86\x47\x57\x17\x1e\x9b\xa9\xd5\xd1\x51\xdf\x02\xf6\x41\x4e\x3f\x83\x84\x18\x08\xbd\x9d\x26\x54\x08\x79\x23\x88\x51\x44\xc1\x25\xa1\xa6\x77\xba\xe2\xe9\x82\x8d\xd6\x08\x6d\x77\x9c\x6a\x84\x5a\xef\x20\x5e\x87\x9e\x70\x80\x09\x17\x2e\xc8\xf2\x89\xd8\x87\x61\x40\x80\xbd\x58\x9c\x1e\x20\x98\x01\x8b\xb3\xb8\xe0\x82\x4c\xd0\x0e\xf5\x9c\x41\x70\xde\x18\x23\x02\x79\x05\x9b\x58\x5d\xed\xbe\x74\x69\x2e\x82\xe6\x13\x60\xbb\x20\xfb\xfb\x81\x77\x99\x56\x2b\x61\x3a\x19\x40\xfc\x84\x80\xd7\xa8\x03\x04\x54\x67\xbe\x90\x4b\x2f\x5c\x69\xea\x00\x16\x30\x0e\x67\x89\x2b\x63\xe2\xd1\x35\x50\xb0\x36\x38\x22\x91\x92\x5d\x99\xfc\x0d\x77\x76\x80\x9a\xef\x48\x6e\x94\xae\x77\x81\x01\x50\x05\x0f\x62\xda\xb0\x41\xbb\x2c\x31\xd3\x47\xa0\x26\xeb\x7d\xc1\x15\x40\x17\x00\xcf\x13\x19\xda\xf5\x07\xae\xf0\x86\x06\xd5\x30\x05\x6e\x50\x1c\xc3\x4b\xd0\x8c\x12\x38\xf3\xc6\xe6\x8d\x99\x16\xf0\x0c\x57\x6c\x7e\xc7\xdc\xac\x2e\x99\xe6\xec\x38\x08\x9b\x63\x0f\x2e\x81\x39\xb8\x64\xc4\x0e\x1c\x58\xd0\xb0\x10\xf1\xfd\x70\x99\x99\x8c\x7c\x9e\x20\xc5\x1e\x40\xab\x23\xea\x77\xc4\x9b\xe4\xc6\xba\x20\xc0\xb1\x6a\xd2\x0b\xc9\x54\x92\xa3\x02\x39\xbf\x15\x60\xf7\x81\x14\xdd\x87\xcb\x12\x2f\x11\xfc\x7e\xc0\x20\x0f\x98\x46\x09\x4e\xbd\x83\xba\x96\x08\x78\x12\xaa\xcb\x15\xde\xdb\xe0\xc6\x4e\x54\x6d\x3b\x17\x55\xd6\xa0\xc3\x11\x66\x50\x82\x50\x95\xc0\x9d\x08\x00\x1e\x10\x22\x5b\x06\x69\x82\xc9\x4d\x2e\x95\x57\xc3\x2c\xaa\x88\x4d\xb7\x8e\x46\xd2\xfd\x9c\x70\x71\xb6\xc8\x87\xcb\x07\x36\xe8\x05\x6a\xb1\xc0\x27\x18\x58\x86\xbb\x8b\x30\x15\xcb\x11\x32\x93\x64\xbc\xe4\x1b\x6e\xd3\x67\x7f\x4a\xb7\xce\xc2\xa5\xe3\x33\x34\x3c\x62\x0d\x96\xc7\x88\x85\xfa\xdd\x0d\x38\x30\xa2\xf2\x62\x78\x70\x75\x79\xb5\xca\x55\x94\x04\x4c\x6b\xc2\xc4\xe2\x52\x6a\xab\x6b\xc1\x79\x23\x77\x60\x05\xd7\x6f\x20\xfb\xe1\x3c\x47\xeb\xc6\x9c\x74\xcc\x1b\xa8\xb5\x99\xce\x0b\xcc\x50\xe0\xbd\x58\xdd\x6e\x22\x54\x6a\x06\x15\x35\x81\xdc\x90\x99\x2d\x5d\xc5\xa6\x70\x00\x16\x40\x6b\x1b\xe0\x48\x05\x96\x67\xe5\xeb\x03\x0b\x1d\xa0\x19\x02\xce\xc1\x2c\xd1\x0c\x8e\x91\x1c\x66\x34\x2e\xa9\xc2\x1c\x23\xb9\x00\x4b\x6d\x16\x5a\x2f\x6c\x36\x22\xd3\xaa\xa0\x3a\x3b\x22\x3f\xfa\xd4\x15\x9c\x3a\x41\x4d\xd9\xac\x58\x01\xa0\x8c\xe2\x76\x98\xef\xe1\xa8\x2e\xeb\x77\x0d\xd2\x5b\x8c\x0d\xc4\x95\xca\x58\x43\xb8\x20\x0f\x78\x41\x5d\x23\x21\xb2\x3c\x90\xb5\xbe\x46\x50\x48\xf6\xee\x50\xfd\x4e\xd4\xda\x0e\x04\x17\x27\x8a\x9b\xec\xc6\x4a\xe0\x14\x2c\x46\xd2\x3b\x04\x00\xe4\x0a\xf2\x24\x2e\x33\x35\x40\xb8\xc6\x27\x46\xac\x91\xcc\xd3\x12\x1f\xa5\x6b\xb6\x0a\x28\xea\x5c\x71\x20\x95\x55\x86\x7e\xe9\x8b\x33\xc0\xea\x80\xda\x24\xa8\xd3\x82\x0d\x97\x00\xb1\x02\x2e\xae\x43\x71\x95\x02\x99\x71\x2a\x09\x55\x9e\x0b\x28\x6a\xe1\xf2\x09\x7c\xb7\x43\xc2\xc5\xd7\x28\x18\x7d\xd6\xae\x41\x8b\xb1\x0a\xdb\x72\x32\xed\xa4\x07\xad\xdd\x5c\x3f\xd0\x08\x64\x66\x5d\x6f\x72\x6a\xfc\x72\xe9\xaa\x87\x23\x2a\x74\x97\x01\x44\x85\x79\x84\x19\xc5\xcd\x16\xd7\xd9\x5f\x70\x83\x90\x12\xf2\x92\x9e\x09\x0a\x4c\x72\x82\xe9\xb1\x09\x34\x33\x4e\xdf\xc1\xe6\x6a\xd7\x94\xc4\xf6\x94\xe0\xda\x6a\x3b\x3d\x49\x26\x54\xde\xd8\xfc\x81\x14\x9b\x09\xee\xed\x28\xec\x0b\xa6\x26\xe0\x12\x6c\x8e\xfa\x46\xf3\xf5\xe6\x36\x93\x21\xd8\xe4\xdc\xe5\x00\x2d\x9d\x10\x88\xcd\x22\x52\x17\xc8\xc4\x1c\xae\x1a\x9b\x5d\x17\x57\x74\xcf\x19\x08\x5e\x71\xed\x0e\x15\x3c\x51\x20\xf9\x44\x5e\x3f\xb9\xbe\x81\x19\x45\x8b\x13\xc2\x3d\xe0\x21\x76\x34\x3e\xb1\x5c\xee\xf3\x91\x33\xa8\xee\x0b\x60\x2e\xd8\xcb\x84\xac\xd7\x5f\x1d\x9f\xc1\xd5\xe9\x36\xd7\xb5\xc9\xfa\x5c\x0b\xb2\x52\xaa\xba\x6f\x8a\x27\x23\x62\x00\x36\x51\xcd\xa8\xfe\x18\x3e\x8c\xa8\xfa\x9c\xaa\x29\x02\x6f\xd3\xa2\x96\xb8\xbb\x76\x9d\xda\x01\x2c\x35\x77\x41\x7f\xe3\xee\x1a\x3a\xac\xa7\xe1\xb7\x31\xc1\xc3\x15\x0d\x08\x3a\xd9\xb5\xb0\x65\x78\x8a\x15\x51\x56\x46\x51\x23\x38\xb7\xca\x9e\x9f\x05\x1d\x24\x3d\x04\x65\xf7\x2e\x88\x39\x26\x74\x8f\xef\x30\x8d\x07\xf2\x07\x0d\x90\x3e\x83\x2c\x25\xb4\xc3\x1a\x4e\xd5\xd8\xa7\xad\x6b\x89\x40\xc8\x84\x9e\x7e\x06\x13\xa7\x2b\x81\x9d\x00\xc4\x41\x17\xcd\xae\x8c\x0d\x27\xf8\x44\x00\x62\x9d\x9e\x4f\xb6\xa9\xb9\x34\x56\x04\x4e\x04\x0d\xcd\x17\x78\x87\xec\x50\x7e\xc5\x3b\x2f\xf4\x3a\x2c\x20\x75\x09\x07\x90\x60\xa6\xb5\x65\x84\xae\xae\x53\x2c\xba\x82\xe3\xe8\x72\x7e\xec\xf3\x59\xa0\x5e\x1d\x16\x18\x00\x6a\x77\xd7\x11\x52\x38\x92\x53\x80\xd0\x14\x20\x41\x19\xd9\x9d\x13\x4c\x73\x81\x1b\x5e\x50\x43\xb8\x80\x7f\x6c\x88\x02\x2d\x0c\x9e\x51\x3e\x33\x8d\x0e\x11\xc8\xfe\x00\xdd\xb5\x74\xf7\x05\x51\x5c\x85\x8d\x07\xd7\x57\x9c\xa1\x3d\x6f\x47\x43\x7b\x57\xba\x9e\x5c\x35\x89\xe4\x2a\xc2\x64\x28\xc4\x17\xfa\x0f\x7a\xb8\xc9\x0d\x6a\x3e\x40\xb3\x6a\x68\x14\xab\xc1\x33\xb4\x40\x44\x24\x19\x70\x7c\x23\x1a\x13\xd7\x29\xba\x3a\x96\x66\x9b\x3f\x61\x0e\x1d\xea\xf5\xe9\x87\x5c\x1c\x91\xbb\x41\x32\x93\x6c\x8f\x7d\x7f\xc1\xf1\x8f\x1d\x49\xda\x8c\x1f\x51\x74\x18\xff\x84\xfa\x57\xae\x3f\x28\x39\xd0\x36\xc0\xff\xa3\x01\xcc\xb9\x77\xf8\xd5\xc5\x08\xda\x58\x20\x11\xc9\x9e\xe2\x06\x41\x88\xa9\xe8\x0a\x8a\xcd\x3e\x82\xd3\x2d\xa6\xc0\xe6\x03\xdc\xd9\x08\x95\x0e\x48\x7c\xb0\xc3\xfc\xe2\xbc\x0a\x93\xad\x25\x82\xa3\x1a\xa4\xa9\x72\x6c\x31\x47\xd7\xd4\x10\xf1\xd0\x02\x92\xc1\xf6\xf9\xc9\xd5\x06\x5f\xe8\xbe\xf8\xd2\xfb\xbf\x50\xab\xab\xe5\xef\x5f\x89\x08\x2a\x43\x15\x52\x5f\x6c\xd7\x59\xd1\x25\xde\x41\x6b\x4a\x57\x60\x89\x40\xf9\x16\xe6\xc0\xa6\x88\xd2\x71\x1a\x16\x57\xda\x08\x2e\x2e\xb0\xcf\x01\x54\x32\xa1\x43\x24\xc1\x36\x05\xa9\x6b\xf6\xed\x2d\xae\x5a\x94\xdd\x49\xac\xae\x4c\x76\x3a\xee\x51\x40\xcb\x58\x2a\x2f\xf1\xe3\x07\x3b\x8c\x15\x05\x56\x03\xe5\x01\x59\xeb\x04\xe1\x3c\x71\x68\x7f\xea\x0a\x11\x68\x3c\xa2\xbf\x2e\xa1\xb2\xeb\x55\x94\x1d\x60\x33\x3c\x43\x06\x25\xde\x5c\x34\xc8\xae\xed\x84\x45\x0c\xb0\xc9\x37\x4e\x91\xe4\x26\x40\x43\x4e\xb8\x34\x76\xa9\xcf\x08\x7a\xaa\x1f\x92\xaf\xec\x70\x63\xe9\x2d\xb2\x3f\xc0\xab\xe7\x82\x1f\x3b\xcc\x38\x55\x86\x99\x11\x69\xf0\x1d\xa7\x7c\xa2\xb8\x1a\x40\x4b\x83\x6b\x8d\x3b\x21\xfc\x0b\xdc\x22\x69\xee\x6f\x84\xb1\x3e\x1f\x11\x91\x11\xca\x20\x62\x77\xb4\xdf\xcc\x30\x63\xae\x67\x37\x13\x68\x5d\x8a\xea\xd0\x09\xe9\x3e\x76\xc7\x0f\x46\x70\xfa\x80\xd4\xb8\x65\x81\x16\xa4\xca\x99\xe4\xc8\x40\xf8\x16\xaa\x5d\xbb\x5e\xe2\xec\xba\xbd\x3c\xa5\xbd\x23\x5b\xd4\xb9\x0c\x51\x46\x69\x5d\x5c\x5b\xbd\xdd\xef\x08\x82\xbb\x6b\x42\x30\x35\x9d\xe0\x09\x56\xf9\xfb\x57\x5d\x03\x3c\xc7\x02\x21\x4c\x6a\x02\x35\x9a\x9f\x36\x2c\x76\x83\x1b\x23\x7d\xe5\x2f\x4a\x0a\xd7\xdb\x13\x5d\x5d\xb1\x00\x36\x25\xd7\xa3\x90\x1c\x9d\x2f\x5d\xc0\x74\x07\x86\xfc\x71\x83\x2d\xdc\x58\xf1\x43\xda\xc9\xba\xc7\x5a\x9c\x20\x0b\x1d\xac\xb7\xb5\x08\x75\x59\xa6\x0e\x10\x18\x8b\x2f\xd9\xb9\xcc\xdd\x69\xdc\x13\x59\x67\x33\xa9\x2b\xaa\x44\x34\xc9\x09\x58\xd3\x01\xbb\xc8\x26\xaf\xc0\xab\x49\x44\x5e\x11\x7e\x7f\x40\x80\x53\xe0\x94\x03\xea\xeb\xc9\xd9\xe0\x0c\xa2\xb4\x43\x65\x27\xd8\x68\x01\x04\x7e\xfc\xd0\xba\x52\xe1\xf9\x16\xa5\x36\x12\x23\x56\x83\x6b\x04\x37\x95\x63\x83\xb3\x3b\x04\xeb\x48\x99\xc0\x1b\x46\xe4\x22\xfa\xc8\x5a\xb9\xcb\x99\x95\x6e\xb1\x6f\x14\x45\x0e\x54\x6a\x76\x97\x13\xbc\xc8\xff\xfe\xf8\xaa\xc0\x82\x17\x07\xa8\xec\x4a\xb9\xbb\xdc\xc3\xdb\x9d\x78\x03\xd3\xdc\x61\x52\x07\xb0\x24\xff\xd0\xd5\x56\x03\x1c\x4b\x80\x7c\x64\x73\x31\xab\x36\x6c\x56\x64\x58\xb9\x22\x91\x69\xc1\xc8\x17\xcc\xa1\xc3\x09\xda\xa4\xdf\x00\xcc\x0d\x27\x11\xc1\x0e\x2f\xae\x11\xe3\x81\xee\x90\xc6\xc5\x16\x8c\x1c\x93\xeb\x71\x2a\x71\x78\xb3\xfc\xfd\x63\xac\x09\x1d\x22\xe6\x7d\xd2\xbf\x07\xe4\x68\x00\x10\x3b\x06\x69\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\x01\x00\x00\xff\xff\x91\x78\x0d\x81\x26\x0f\x00\x00") - -func webdataTexPngBytes() ([]byte, error) { - return bindataRead( - _webdataTexPng, - "webdata/tex.png", - ) -} - -func webdataTexPng() (*asset, error) { - bytes, err := webdataTexPngBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "webdata/tex.png", size: 3878, mode: os.FileMode(436), modTime: time.Unix(1, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -// Asset loads and returns the asset for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func Asset(name string) ([]byte, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) - } - return a.bytes, nil - } - return nil, fmt.Errorf("Asset %s not found", name) -} - -// MustAsset is like Asset but panics when Asset would return an error. -// It simplifies safe initialization of global variables. -func MustAsset(name string) []byte { - a, err := Asset(name) - if err != nil { - panic("asset: Asset(" + name + "): " + err.Error()) - } - - return a -} - -// AssetInfo loads and returns the asset info for the given name. -// It returns an error if the asset could not be found or -// could not be loaded. -func AssetInfo(name string) (os.FileInfo, error) { - cannonicalName := strings.Replace(name, "\\", "/", -1) - if f, ok := _bindata[cannonicalName]; ok { - a, err := f() - if err != nil { - return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) - } - return a.info, nil - } - return nil, fmt.Errorf("AssetInfo %s not found", name) -} - -// AssetNames returns the names of the assets. -func AssetNames() []string { - names := make([]string, 0, len(_bindata)) - for name := range _bindata { - names = append(names, name) - } - return names -} - -// _bindata is a table, holding each asset generator, mapped to its name. -var _bindata = map[string]func() (*asset, error){ - "webdata/fn.js": webdataFnJs, - "webdata/icon.png": webdataIconPng, - "webdata/icon.woff": webdataIconWoff, - "webdata/index.html": webdataIndexHtml, - "webdata/main.css": webdataMainCss, - "webdata/sha256.js": webdataSha256Js, - "webdata/tex.png": webdataTexPng, -} - -// AssetDir returns the file names below a certain -// directory embedded in the file by go-bindata. -// For example if you run go-bindata on data/... and data contains the -// following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png -// then AssetDir("data") would return []string{"foo.txt", "img"} -// AssetDir("data/img") would return []string{"a.png", "b.png"} -// AssetDir("foo.txt") and AssetDir("notexist") would return an error -// AssetDir("") will return []string{"data"}. -func AssetDir(name string) ([]string, error) { - node := _bintree - if len(name) != 0 { - cannonicalName := strings.Replace(name, "\\", "/", -1) - pathList := strings.Split(cannonicalName, "/") - for _, p := range pathList { - node = node.Children[p] - if node == nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - } - } - if node.Func != nil { - return nil, fmt.Errorf("Asset %s not found", name) - } - rv := make([]string, 0, len(node.Children)) - for childName := range node.Children { - rv = append(rv, childName) - } - return rv, nil -} - -type bintree struct { - Func func() (*asset, error) - Children map[string]*bintree -} - -var _bintree = &bintree{nil, map[string]*bintree{ - "webdata": &bintree{nil, map[string]*bintree{ - "fn.js": &bintree{webdataFnJs, map[string]*bintree{}}, - "icon.png": &bintree{webdataIconPng, map[string]*bintree{}}, - "icon.woff": &bintree{webdataIconWoff, map[string]*bintree{}}, - "index.html": &bintree{webdataIndexHtml, map[string]*bintree{}}, - "main.css": &bintree{webdataMainCss, map[string]*bintree{}}, - "sha256.js": &bintree{webdataSha256Js, map[string]*bintree{}}, - "tex.png": &bintree{webdataTexPng, map[string]*bintree{}}, - }}, -}} - -// RestoreAsset restores an asset under the given directory -func RestoreAsset(dir, name string) error { - data, err := Asset(name) - if err != nil { - return err - } - info, err := AssetInfo(name) - if err != nil { - return err - } - err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) - if err != nil { - return err - } - err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) - if err != nil { - return err - } - err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) - if err != nil { - return err - } - return nil -} - -// RestoreAssets restores an asset under the given directory recursively -func RestoreAssets(dir, name string) error { - children, err := AssetDir(name) - // File - if err != nil { - return RestoreAsset(dir, name) - } - // Dir - for _, child := range children { - err = RestoreAssets(dir, filepath.Join(name, child)) - if err != nil { - return err - } - } - return nil -} - -func _filePath(dir, name string) string { - cannonicalName := strings.Replace(name, "\\", "/", -1) - return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) -} - -func assetFS() *assetfs.AssetFS { - assetInfo := func(path string) (os.FileInfo, error) { - return os.Stat(path) - } - for k := range _bintree.Children { - return &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, AssetInfo: assetInfo, Prefix: k} - } - panic("unreachable") -} diff --git a/bindata.go b/bindata.go deleted file mode 100644 index f81829b..0000000 --- a/bindata.go +++ /dev/null @@ -1,4 +0,0 @@ -package main - -//go:generate go-bindata-assetfs -mode 0664 -modtime 1 -o ./bindata.generated.go webdata/... -//go:generate gofmt -w ./bindata.generated.go diff --git a/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml b/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml index 84ab1a9..113ef4d 100644 --- a/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml +++ b/desktop/com.github.unrud.RemoteTouchpad.metainfo.xml @@ -17,8 +17,190 @@ com.github.unrud.RemoteTouchpad.desktop https://github.com/Unrud/remote-touchpad/issues https://github.com/Unrud/remote-touchpad - Unrud + https://github.com/Unrud/remote-touchpad + Unrud + + Unrud + + + +
    +
  • Improve X11 keyboard input
  • +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Add restore token support for the remote desktop portal
  • +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Prevent incorrect MIME types
  • +
+
+
+ + +
    +
  • Keep text input open after sending
  • +
  • Workaround for virtual keyboard covering buttons (Chromium & iOS)
  • +
  • Enable hardware keyboard input in mouse mode
  • +
+
+
+ + +
    +
  • Fix Xwayland detection on newer servers
  • +
  • Fix support for the KDE remote desktop portal
  • +
  • Set correct minimum version for Go
  • +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Fix Windows support
  • +
+
+
+ + +
    +
  • uinput: Fix vertical scroll direction
  • +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Add feedback to buttons
  • +
  • Add numeric keypad
  • +
  • Prevent touch triggering mouse mode
  • +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Initial support for uinput
  • +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Improve XWayland detection
  • +
  • Update dependencies
  • +
  • Flatpak: Add Wayland socket to disable X11 socket
  • +
+
+
+ + +
    +
  • Update dependencies
  • +
  • Fix race in RemoteDesktop portal controller
  • +
+
+
+ + +
    +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Add command-line options for move and scroll speed
  • +
  • Add hardware mouse support
  • +
  • Limit update rate
  • +
  • Windows: Reverse vertical scroll direction
  • +
  • Show error message if compiled without controller
  • +
  • Don't show disabled controllers in "unsupported platform" error message
  • +
  • Switch from go-bindata-assetfs to go:embed
  • +
+
+
+ + +
    +
  • Add more shortcut keys (Arrows, Delete, Enter, …)
  • +
  • Add basic support for physical keyboard
  • +
  • Allow fullscreen on keys page
  • +
  • Enable touchpad on keys page
  • +
  • Retain keyboard text when reloading website
  • +
  • Improve compatibility with old browsers
  • +
  • Write URL and QR code to stdout
  • +
  • Ignore unparseable IP
  • +
  • Remove trailing new line from QR code
  • +
+
+
+ + +
    +
  • Use relative URL for websocket
  • +
  • Update dependencies
  • +
+
+
+ + +
    +
  • Disable browser touch gestures
  • +
  • Update dependencies
  • +
+
+
    @@ -191,16 +373,16 @@ - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.0.3/screenshots/1.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.5.4/screenshots/1.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.0.3/screenshots/2.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.5.4/screenshots/2.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.0.3/screenshots/3.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.5.4/screenshots/3.png - https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.0.3/screenshots/4.png + https://raw.githubusercontent.com/Unrud/remote-touchpad/v1.5.4/screenshots/4.png diff --git a/flatpak/com.github.unrud.RemoteTouchpad.yaml b/flatpak/com.github.unrud.RemoteTouchpad.yaml index 74e1d5e..239160e 100644 --- a/flatpak/com.github.unrud.RemoteTouchpad.yaml +++ b/flatpak/com.github.unrud.RemoteTouchpad.yaml @@ -1,27 +1,24 @@ app-id: com.github.unrud.RemoteTouchpad runtime: org.freedesktop.Platform -runtime-version: '21.08' +runtime-version: '25.08' sdk: org.freedesktop.Sdk command: remote-touchpad sdk-extensions: - org.freedesktop.Sdk.Extension.golang finish-args: - - --socket=x11 - --socket=fallback-x11 + - --socket=wayland + - --share=ipc # Required by Flathub linter - --share=network modules: - name: remote-touchpad sources: - # Vendor dependencies: go mod vendor -v - type: git - url: https://github.com/elazarl/go-bindata-assetfs - tag: v1.0.1 - commit: 234c15e7648ff35458026de92b34c637bae5e6f7 - dest: vendor/github.com/elazarl/go-bindata-assetfs + path: .. - type: git url: https://github.com/godbus/dbus - tag: v5.0.5 - commit: 84a2dfb382a81e88c000206b8fcba8d98f00ad1e + tag: v5.2.2 + commit: a8ac15ba63645f02ffd57f4b443203279ab40b30 dest: vendor/github.com/godbus/dbus/v5 - type: git url: https://github.com/skip2/go-qrcode @@ -29,14 +26,17 @@ modules: dest: vendor/github.com/skip2/go-qrcode - type: git url: https://github.com/golang/net - commit: 978cfadd31cf6299758dbb5165d451ce91989f1a + tag: v0.57.0 + commit: b8f09f6f062ceb4531b7af4bd17a5c8fe9c4b2b5 dest: vendor/golang.org/x/net + - type: git + url: https://github.com/golang/sys + tag: v0.47.0 + commit: 9e7e939dcafac07e8ab4cffa6e5fc74908413f00 + dest: vendor/golang.org/x/sys - type: file path: modules.txt dest: vendor - - type: git - path: .. - branch: master buildsystem: simple build-commands: - . /usr/lib/sdk/golang/enable.sh; go build -tags portal,x11 diff --git a/flatpak/modules.txt b/flatpak/modules.txt index f982cf6..6092904 100644 --- a/flatpak/modules.txt +++ b/flatpak/modules.txt @@ -1,14 +1,17 @@ -# github.com/elazarl/go-bindata-assetfs v1.0.1 -## explicit -github.com/elazarl/go-bindata-assetfs -# github.com/godbus/dbus/v5 v5.0.5 -## explicit +# github.com/bendahl/uinput v1.7.0 +## explicit; go 1.13 +github.com/bendahl/uinput +# github.com/godbus/dbus/v5 v5.2.2 +## explicit; go 1.20 github.com/godbus/dbus/v5 # github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e -## explicit +## explicit; go 1.13 github.com/skip2/go-qrcode github.com/skip2/go-qrcode/bitset github.com/skip2/go-qrcode/reedsolomon -# golang.org/x/net v0.0.0-20210917221730-978cfadd31cf -## explicit +# golang.org/x/net v0.57.0 +## explicit; go 1.25.0 golang.org/x/net/websocket +# golang.org/x/sys v0.47.0 +## explicit; go 1.25.0 +golang.org/x/sys/unix diff --git a/go.mod b/go.mod index 11bce4f..5afd19d 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ module github.com/unrud/remote-touchpad -go 1.15 +go 1.25.5 require ( - github.com/elazarl/go-bindata-assetfs v1.0.1 - github.com/godbus/dbus/v5 v5.0.5 + github.com/bendahl/uinput v1.7.0 + github.com/godbus/dbus/v5 v5.2.2 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - golang.org/x/net v0.0.0-20210917221730-978cfadd31cf + golang.org/x/net v0.57.0 ) + +require golang.org/x/sys v0.47.0 // indirect diff --git a/go.sum b/go.sum index 16c922f..783b4df 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,10 @@ -github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= -github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= -github.com/godbus/dbus/v5 v5.0.5 h1:9Eg0XUhQxtkV8ykTMKtMMYY72g4NgxtRq4jgh4Ih5YM= -github.com/godbus/dbus/v5 v5.0.5/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/bendahl/uinput v1.7.0 h1:nA4fm8Wu8UYNOPykIZm66nkWEyvxzfmJ8YC02PM40jg= +github.com/bendahl/uinput v1.7.0/go.mod h1:Np7w3DINc9wB83p12fTAM3DPPhFnAKP0WTXRqCQJ6Z8= +github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= +github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= diff --git a/host.go b/host.go index 14ed821..622b3bf 100644 --- a/host.go +++ b/host.go @@ -28,7 +28,7 @@ import ( const ipv4Rating int = +1 -func FindDefaultHost() string { +func findDefaultHost() string { type hostsValue struct { prio int host string @@ -53,10 +53,9 @@ func FindDefaultHost() string { panic(err) } ip := net.ParseIP(host) - if ip == nil { - panic("Invalid IP address: " + host) + if ip != nil { + addIP(100, ip) } - addIP(100, ip) } interfaces, _ := net.Interfaces() for _, inter := range interfaces { @@ -75,7 +74,8 @@ func FindDefaultHost() string { continue } for _, linkLocalPrefix := range []string{ - "169.254.", "fe8", "fe9", "fea", "feb"} { + "169.254.", "fe8", "fe9", "fea", "feb", + } { if strings.HasPrefix(ip.String(), linkLocalPrefix) { addIP(20, ip) continue addrs diff --git a/icon_font/U+1F507 Speaker with Cancellation Stroke.svg b/icon-font/U+1F507 Speaker with Cancellation Stroke.svg similarity index 100% rename from icon_font/U+1F507 Speaker with Cancellation Stroke.svg rename to icon-font/U+1F507 Speaker with Cancellation Stroke.svg diff --git a/icon_font/U+1F509 Speaker with One Sound Wave.svg b/icon-font/U+1F509 Speaker with One Sound Wave.svg similarity index 100% rename from icon_font/U+1F509 Speaker with One Sound Wave.svg rename to icon-font/U+1F509 Speaker with One Sound Wave.svg diff --git a/icon_font/U+1F50A Speaker with Three Sound Waves.svg b/icon-font/U+1F50A Speaker with Three Sound Waves.svg similarity index 100% rename from icon_font/U+1F50A Speaker with Three Sound Waves.svg rename to icon-font/U+1F50A Speaker with Three Sound Waves.svg diff --git a/icon_font/U+1FA9F Window.svg b/icon-font/U+1FA9F Window.svg similarity index 100% rename from icon_font/U+1FA9F Window.svg rename to icon-font/U+1FA9F Window.svg diff --git a/icon_font/U+2B60 Leftwards Triangle-Headed Arrow.svg b/icon-font/U+2190 Leftwards Arrow.svg similarity index 100% rename from icon_font/U+2B60 Leftwards Triangle-Headed Arrow.svg rename to icon-font/U+2190 Leftwards Arrow.svg diff --git a/icon-font/U+2191 Upwards Arrow.svg b/icon-font/U+2191 Upwards Arrow.svg new file mode 100644 index 0000000..f6e7e6a --- /dev/null +++ b/icon-font/U+2191 Upwards Arrow.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon_font/U+2B62 Rightwards Triangle-Headed Arrow.svg b/icon-font/U+2192 Rightwards Arrow.svg similarity index 100% rename from icon_font/U+2B62 Rightwards Triangle-Headed Arrow.svg rename to icon-font/U+2192 Rightwards Arrow.svg diff --git a/icon-font/U+2193 Downwards Arrow.svg b/icon-font/U+2193 Downwards Arrow.svg new file mode 100644 index 0000000..001042c --- /dev/null +++ b/icon-font/U+2193 Downwards Arrow.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon_font/U+21BB Clockwise Open Circle Arrow.svg b/icon-font/U+21BB Clockwise Open Circle Arrow.svg similarity index 100% rename from icon_font/U+21BB Clockwise Open Circle Arrow.svg rename to icon-font/U+21BB Clockwise Open Circle Arrow.svg diff --git a/icon_font/U+2630 Trigram for Heaven.svg b/icon-font/U+2261 Identical To.svg similarity index 91% rename from icon_font/U+2630 Trigram for Heaven.svg rename to icon-font/U+2261 Identical To.svg index 4c0df4e..bdf2d84 100644 --- a/icon_font/U+2630 Trigram for Heaven.svg +++ b/icon-font/U+2261 Identical To.svg @@ -1,19 +1,19 @@ + sodipodi:docname="U+2261 Identical To .svg" + inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + inkscape:measure-end="406.912,580.422" + inkscape:pagecheckerboard="0"> diff --git a/icon-font/U+2326 Erase To the Right.svg b/icon-font/U+2326 Erase To the Right.svg new file mode 100644 index 0000000..83f7216 --- /dev/null +++ b/icon-font/U+2326 Erase To the Right.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon_font/U+2328 Keyboard.svg b/icon-font/U+2328 Keyboard.svg similarity index 100% rename from icon_font/U+2328 Keyboard.svg rename to icon-font/U+2328 Keyboard.svg diff --git a/icon-font/U+232B Erase To the Left.svg b/icon-font/U+232B Erase To the Left.svg new file mode 100644 index 0000000..1543cdd --- /dev/null +++ b/icon-font/U+232B Erase To the Left.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icon_font/U+23ED Black Right-Pointing Double Triangle with Vertical Bar.svg b/icon-font/U+23ED Black Right-Pointing Double Triangle with Vertical Bar.svg similarity index 100% rename from icon_font/U+23ED Black Right-Pointing Double Triangle with Vertical Bar.svg rename to icon-font/U+23ED Black Right-Pointing Double Triangle with Vertical Bar.svg diff --git a/icon_font/U+23EE Black Left-Pointing Double Triangle with Vertical Bar.svg b/icon-font/U+23EE Black Left-Pointing Double Triangle with Vertical Bar.svg similarity index 100% rename from icon_font/U+23EE Black Left-Pointing Double Triangle with Vertical Bar.svg rename to icon-font/U+23EE Black Left-Pointing Double Triangle with Vertical Bar.svg diff --git a/icon_font/U+23EF Black Right-Pointing Triangle with Double Vertical Bar.svg b/icon-font/U+23EF Black Right-Pointing Triangle with Double Vertical Bar.svg similarity index 100% rename from icon_font/U+23EF Black Right-Pointing Triangle with Double Vertical Bar.svg rename to icon-font/U+23EF Black Right-Pointing Triangle with Double Vertical Bar.svg diff --git a/icon_font/U+27A3 Three-D Bottom-Lighted Rightwards Arrowhead.svg b/icon-font/U+27A3 Three-D Bottom-Lighted Rightwards Arrowhead.svg similarity index 100% rename from icon_font/U+27A3 Three-D Bottom-Lighted Rightwards Arrowhead.svg rename to icon-font/U+27A3 Three-D Bottom-Lighted Rightwards Arrowhead.svg diff --git a/icon_font/U+2922 North East and South West Arrow.svg b/icon-font/U+2922 North East and South West Arrow.svg similarity index 100% rename from icon_font/U+2922 North East and South West Arrow.svg rename to icon-font/U+2922 North East and South West Arrow.svg diff --git a/icon-font/U+2B60 Leftwards Triangle-Headed Arrow.svg b/icon-font/U+2B60 Leftwards Triangle-Headed Arrow.svg new file mode 100644 index 0000000..6364869 --- /dev/null +++ b/icon-font/U+2B60 Leftwards Triangle-Headed Arrow.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon-font/U+2B62 Rightwards Triangle-Headed Arrow.svg b/icon-font/U+2B62 Rightwards Triangle-Headed Arrow.svg new file mode 100644 index 0000000..f13e555 --- /dev/null +++ b/icon-font/U+2B62 Rightwards Triangle-Headed Arrow.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon-font/U+2B70 Leftwards Triangle-Headed Arrow To Bar.svg b/icon-font/U+2B70 Leftwards Triangle-Headed Arrow To Bar.svg new file mode 100644 index 0000000..a92cc25 --- /dev/null +++ b/icon-font/U+2B70 Leftwards Triangle-Headed Arrow To Bar.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon-font/U+2B72 Rightwards Triangle-Headed Arrow To Bar.svg b/icon-font/U+2B72 Rightwards Triangle-Headed Arrow To Bar.svg new file mode 100644 index 0000000..33c8d85 --- /dev/null +++ b/icon-font/U+2B72 Rightwards Triangle-Headed Arrow To Bar.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/icon-font/U+2B82 Rightwards Triangle-Headed Arrow Over Leftwards Triangle-Headed Arrow.svg b/icon-font/U+2B82 Rightwards Triangle-Headed Arrow Over Leftwards Triangle-Headed Arrow.svg new file mode 100644 index 0000000..3cdb17f --- /dev/null +++ b/icon-font/U+2B82 Rightwards Triangle-Headed Arrow Over Leftwards Triangle-Headed Arrow.svg @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/icon-font/U+2BA0 Downwards Triangle-Headed Arrow with Long Tip Leftwards.svg b/icon-font/U+2BA0 Downwards Triangle-Headed Arrow with Long Tip Leftwards.svg new file mode 100644 index 0000000..8075176 --- /dev/null +++ b/icon-font/U+2BA0 Downwards Triangle-Headed Arrow with Long Tip Leftwards.svg @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon-font/U+2BC7 Black Medium Left-Pointing Triangle Centred.svg b/icon-font/U+2BC7 Black Medium Left-Pointing Triangle Centred.svg new file mode 100644 index 0000000..26afd5b --- /dev/null +++ b/icon-font/U+2BC7 Black Medium Left-Pointing Triangle Centred.svg @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/icon-font/icon.sfd b/icon-font/icon.sfd new file mode 100644 index 0000000..34d0889 --- /dev/null +++ b/icon-font/icon.sfd @@ -0,0 +1,1091 @@ +SplineFontDB: 3.2 +FontName: Icon +FullName: Icon +FamilyName: Icon +Weight: Regular +Copyright: Copyright (c) 2018 Unrud +UComments: "2018-8-29: Created with FontForge (http://fontforge.org)" +Version: 001.000 +ItalicAngle: 0 +UnderlinePosition: -102.4 +UnderlineWidth: 51.2 +Ascent: 819 +Descent: 205 +InvalidEm: 0 +LayerCount: 2 +Layer: 0 0 "Back" 1 +Layer: 1 0 "Zeichen" 0 +XUID: [1021 32 396054688 5708680] +FSType: 0 +OS2Version: 0 +OS2_WeightWidthSlopeOnly: 0 +OS2_UseTypoMetrics: 1 +CreationTime: 1535548659 +ModificationTime: 1643323396 +PfmFamily: 17 +TTFWeight: 400 +TTFWidth: 5 +LineGap: 92 +VLineGap: 0 +OS2TypoAscent: 0 +OS2TypoAOffset: 1 +OS2TypoDescent: 0 +OS2TypoDOffset: 1 +OS2TypoLinegap: 92 +OS2WinAscent: 0 +OS2WinAOffset: 1 +OS2WinDescent: 0 +OS2WinDOffset: 1 +HheadAscent: 0 +HheadAOffset: 1 +HheadDescent: 0 +HheadDOffset: 1 +OS2Vendor: 'PfEd' +MarkAttachClasses: 1 +DEI: 91125 +LangName: 1033 +Encoding: UnicodeFull +Compacted: 1 +UnicodeInterp: none +NameList: AGL For New Fonts +DisplaySize: -48 +AntiAlias: 1 +FitToEm: 0 +WinInfo: 0 35 12 +BeginPrivate: 0 +EndPrivate +TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144 +BeginChars: 1114112 25 + +StartChar: uni2328 +Encoding: 9000 9000 0 +Width: 1024 +VWidth: 0 +Flags: W +HStem: 10 82<256 768> 205 61<292 404 456 568 620 732> 379 51<128 241 292 404 456 568 620 732 783 896> 543 61<128 241 292 404 456 568 620 732 783 896> +VStem: 20 108<266 379 430 543> 241 51<266 379 430 543> 404 52<266 379 430 543> 568 52<266 379 430 543> 732 51<266 379 430 543> 768 236<92 205> 896 108<266 379 430 543> +LayerCount: 2 +Fore +SplineSet +72 604 m 2xff20 + 952 604 l 2 + 981 604 1004 582 1004 553 c 2 + 1004 61 l 2 + 1004 32 981 10 952 10 c 2 + 72 10 l 2 + 43 10 20 32 20 61 c 2 + 20 553 l 2 + 20 582 43 604 72 604 c 2xff20 +138 543 m 2 + 132 543 128 538 128 532 c 2 + 128 440 l 2 + 128 434 132 430 138 430 c 2 + 230 430 l 2 + 236 430 241 434 241 440 c 2 + 241 532 l 2 + 241 538 236 543 230 543 c 2 + 138 543 l 2 +302 543 m 2 + 296 543 292 538 292 532 c 2 + 292 440 l 2 + 292 434 296 430 302 430 c 2 + 394 430 l 2 + 400 430 404 434 404 440 c 2 + 404 532 l 2 + 404 538 400 543 394 543 c 2 + 302 543 l 2 +466 543 m 2 + 460 543 456 538 456 532 c 2 + 456 440 l 2 + 456 434 460 430 466 430 c 2 + 558 430 l 2 + 564 430 568 434 568 440 c 2 + 568 532 l 2 + 568 538 564 543 558 543 c 2 + 466 543 l 2 +630 543 m 2 + 624 543 620 538 620 532 c 2 + 620 440 l 2 + 620 434 624 430 630 430 c 2 + 722 430 l 2 + 728 430 732 434 732 440 c 2 + 732 532 l 2xff80 + 732 538 728 543 722 543 c 2 + 630 543 l 2 +794 543 m 2 + 788 543 783 538 783 532 c 2 + 783 440 l 2 + 783 434 788 430 794 430 c 2 + 886 430 l 2 + 892 430 896 434 896 440 c 2 + 896 532 l 2xffa0 + 896 538 892 543 886 543 c 2 + 794 543 l 2 +138 379 m 2 + 132 379 128 375 128 369 c 2 + 128 276 l 2 + 128 270 132 266 138 266 c 2 + 230 266 l 2 + 236 266 241 270 241 276 c 2 + 241 369 l 2 + 241 375 236 379 230 379 c 2 + 138 379 l 2 +302 379 m 2 + 296 379 292 375 292 369 c 2 + 292 276 l 2 + 292 270 296 266 302 266 c 2 + 394 266 l 2 + 400 266 404 270 404 276 c 2 + 404 369 l 2 + 404 375 400 379 394 379 c 2 + 302 379 l 2 +466 379 m 2 + 460 379 456 375 456 369 c 2 + 456 276 l 2 + 456 270 460 266 466 266 c 2 + 558 266 l 2 + 564 266 568 270 568 276 c 2 + 568 369 l 2 + 568 375 564 379 558 379 c 2 + 466 379 l 2 +630 379 m 2 + 624 379 620 375 620 369 c 2 + 620 276 l 2 + 620 270 624 266 630 266 c 2 + 722 266 l 2 + 728 266 732 270 732 276 c 2 + 732 369 l 2 + 732 375 728 379 722 379 c 2 + 630 379 l 2 +794 379 m 2 + 788 379 783 375 783 369 c 2 + 783 276 l 2 + 783 270 788 266 794 266 c 2 + 886 266 l 2 + 892 266 896 270 896 276 c 2 + 896 369 l 2 + 896 375 892 379 886 379 c 2 + 794 379 l 2 +266 205 m 2 + 260 205 256 201 256 195 c 2 + 256 102 l 2 + 256 96 260 92 266 92 c 2 + 758 92 l 2 + 764 92 768 96 768 102 c 2 + 768 195 l 2xff40 + 768 201 764 205 758 205 c 2 + 266 205 l 2 +EndSplineSet +EndChar + +StartChar: uni2922 +Encoding: 10530 10530 1 +Width: 1024 +VWidth: 0 +Flags: W +HStem: -92 82<253 420> 625 82<604 771> +VStem: 113 82<48 215> 829 82<399 566> +LayerCount: 2 +Fore +SplineSet +614 707 m 2 + 901 707 l 2 + 906 707 911 701 911 696 c 2 + 911 410 l 2 + 911 404 907 399 901 399 c 2 + 840 399 l 2 + 834 399 829 404 829 410 c 2 + 829 566 l 1 + 650 388 l 2 + 646 384 640 384 636 388 c 2 + 593 431 l 2 + 589 435 589 441 593 445 c 2 + 771 625 l 1 + 614 625 l 2 + 609 625 604 630 604 635 c 2 + 604 696 l 2 + 604 702 608 707 614 707 c 2 +410 -92 m 2 + 123 -92 l 2 + 118 -92 113 -87 113 -82 c 2 + 113 205 l 2 + 113 211 117 215 123 215 c 2 + 184 215 l 2 + 190 215 195 211 195 205 c 2 + 195 48 l 1 + 374 226 l 2 + 378 230 384 230 388 226 c 2 + 431 183 l 2 + 435 179 435 173 431 169 c 2 + 253 -10 l 1 + 410 -10 l 2 + 415 -10 420 -15 420 -20 c 2 + 420 -82 l 2 + 420 -88 416 -92 410 -92 c 2 +EndSplineSet +EndChar + +StartChar: uni27A3 +Encoding: 10147 10147 2 +Width: 1024 +VWidth: 0 +Flags: W +LayerCount: 2 +Fore +SplineSet +34 651 m 0 + 35 651 l 0 + 36 651 l 0 + 37 651 l 0 + 39 650 l 2 + 993 342 l 2 + 994 341 l 0 + 995 341 l 0 + 996 340 l 0 + 997 340 l 0 + 998 339 l 0 + 999 338 l 0 + 1000 337 l 0 + 1001 336 l 0 + 1001 334 l 0 + 1002 333 l 0 + 1002 332 l 0 + 1004 330 l 0 + 1004 329 l 0 + 1004 328 l 2 + 1004 327 l 0 + 1004 321 998 314 993 312 c 2 + 39 -36 l 2 + 38 -37 35 -37 34 -37 c 0 + 26 -37 18 -30 18 -22 c 0 + 18 -20 19 -16 20 -14 c 2 + 199 327 l 1 + 199 328 l 1 + 198 328 l 1 + 20 628 l 2 + 20 629 l 0 + 19 630 l 0 + 17 634 18 638 19 642 c 0 + 20 643 l 0 + 20 644 l 0 + 22 645 l 0 + 22 646 l 0 + 24 649 29 651 33 651 c 0 + 34 651 l 0 +231 328 m 2 + 231 327 l 0 + 231 325 231 321 230 319 c 2 + 66 6 l 1 + 941 327 l 1 + 937 328 l 1 + 231 328 l 2 +EndSplineSet +EndChar + +StartChar: uni21BB +Encoding: 8635 8635 3 +Width: 1024 +VWidth: 0 +Flags: W +HStem: -12 82<420.425 611.489> 545 82<415.37 602.162> +VStem: 193 81<210.457 396.738> +LayerCount: 2 +Fore +SplineSet +499 627 m 0 + 502 627 509 627 512 627 c 0 + 615 627 741 554 791 464 c 1 + 834 615 l 2 + 836 620 841 623 846 622 c 2 + 905 605 l 2 + 910 603 914 598 912 593 c 2 + 835 316 l 2 + 834 311 827 308 822 309 c 2 + 546 387 l 2 + 541 388 537 394 539 399 c 2 + 555 459 l 2 + 557 464 563 467 568 466 c 2 + 719 424 l 1 + 681 491 589 545 512 545 c 0 + 478 545 426 530 396 514 c 0 + 329 476 274 384 274 307 c 0 + 274 273 289 221 305 191 c 0 + 343 124 435 70 512 70 c 0 + 546 70 599 84 629 100 c 1 + 669 29 l 1 + 629 6 558 -12 512 -12 c 0 + 409 -12 283 61 233 151 c 0 + 210 191 193 261 193 307 c 0 + 193 410 265 536 355 586 c 0 + 392 606 457 625 499 627 c 0 +EndSplineSet +EndChar + +StartChar: u1F50A +Encoding: 128266 128266 4 +Width: 1024 +VWidth: 0 +Flags: W +HStem: 144 342<20 269> +VStem: 20 492<144 486> 529 100<148.145 215.75 402.021 485.328> 552 107<170.891 440.716> 724 107<170.593 443.765> 896 108<172.375 441.418> +LayerCount: 2 +Fore +SplineSet +497 650 m 0xcc + 505 650 512 643 512 635 c 2 + 512 -20 l 2 + 512 -33 497 -40 487 -32 c 2 + 269 144 l 1 + 36 144 l 2 + 28 144 20 152 20 160 c 2 + 20 471 l 2 + 20 479 28 486 36 486 c 2 + 290 486 l 1 + 487 646 l 2 + 490 648 494 650 497 650 c 0xcc +898 614 m 0 + 903 614 908 612 911 608 c 0 + 967 526 1001 427 1004 321 c 0 + 1004 294 l 2 + 1001 189 967 90 912 8 c 0 + 908 2 901 0 894 3 c 2 + 821 33 l 2 + 812 37 809 47 815 55 c 0 + 866 126 896 213 896 307 c 0 + 896 403 864 490 812 562 c 0 + 806 570 810 582 819 586 c 2 + 893 613 l 2 + 895 614 896 614 898 614 c 0 +758 562 m 0 + 764 562 768 559 771 554 c 0 + 809 480 831 396 831 307 c 0 + 831 221 810 140 774 67 c 0 + 770 60 762 56 755 59 c 2 + 684 89 l 2 + 676 92 672 102 676 110 c 0 + 707 169 724 235 724 307 c 0 + 724 382 705 452 672 512 c 0 + 668 520 671 531 680 534 c 2 + 753 561 l 2 + 755 562 756 562 758 562 c 0 +610 507 m 0 + 616 507 623 503 625 497 c 0 + 648 438 659 374 659 307 c 0xdc + 659 245 648 185 629 129 c 0 + 626 121 616 117 608 120 c 2 + 538 148 l 2 + 531 151 526 160 529 168 c 0xec + 544 212 552 258 552 307 c 0xdc + 552 360 541 412 524 459 c 0 + 521 467 526 476 534 479 c 2 + 605 506 l 2 + 607 507 608 507 610 507 c 0 +EndSplineSet +EndChar + +StartChar: u1F509 +Encoding: 128265 128265 5 +Width: 1024 +VWidth: 0 +Flags: W +HStem: 144 342<20 269> +VStem: 20 492<144 486> 529 100<148.145 215.75 402.021 485.328> 552 107<170.891 440.716> +LayerCount: 2 +Fore +SplineSet +497 650 m 0xc0 + 505 650 512 643 512 635 c 2 + 512 -20 l 2 + 512 -33 497 -40 487 -32 c 2 + 269 144 l 1 + 36 144 l 2 + 28 144 20 152 20 160 c 2 + 20 471 l 2 + 20 479 28 486 36 486 c 2 + 290 486 l 1 + 487 646 l 2 + 490 648 494 650 497 650 c 0xc0 +610 507 m 0 + 616 507 623 503 625 497 c 0 + 648 438 659 374 659 307 c 0xd0 + 659 245 648 185 629 129 c 0 + 626 121 616 117 608 120 c 2 + 538 148 l 2 + 531 151 526 160 529 168 c 0xe0 + 544 212 552 258 552 307 c 0xd0 + 552 360 541 412 524 459 c 0 + 521 467 526 476 534 479 c 2 + 605 506 l 2 + 607 507 608 507 610 507 c 0 +EndSplineSet +EndChar + +StartChar: u1F507 +Encoding: 128263 128263 6 +Width: 1024 +VWidth: 0 +Flags: W +HStem: 144 342<20 269> +VStem: 20 492<144 486> +LayerCount: 2 +Fore +SplineSet +497 650 m 0 + 505 650 512 643 512 635 c 2 + 512 -20 l 2 + 512 -33 497 -40 487 -32 c 2 + 269 144 l 1 + 36 144 l 2 + 28 144 20 152 20 160 c 2 + 20 471 l 2 + 20 479 28 486 36 486 c 2 + 290 486 l 1 + 487 646 l 2 + 490 648 494 650 497 650 c 0 +679 495 m 0 + 683 495 687 493 690 490 c 2 + 815 365 l 1 + 941 490 l 2 + 947 496 957 496 963 490 c 2 + 998 455 l 2 + 1004 449 1004 439 998 433 c 2 + 873 307 l 1 + 998 181 l 2 + 1004 175 1004 166 998 160 c 2 + 963 124 l 2 + 957 118 947 118 941 124 c 2 + 815 250 l 1 + 690 124 l 2 + 684 118 674 118 668 124 c 2 + 632 160 l 2 + 626 166 626 175 632 181 c 2 + 758 307 l 1 + 632 433 l 2 + 626 439 626 449 632 455 c 2 + 668 490 l 2 + 671 493 675 495 679 495 c 0 +EndSplineSet +EndChar + +StartChar: uni23ED +Encoding: 9197 9197 7 +Width: 1024 +VWidth: 0 +Flags: W +VStem: 840 164<-36 288 327 650> +LayerCount: 2 +Fore +SplineSet +988 650 m 2 + 996 650 1004 643 1004 635 c 2 + 1004 -20 l 2 + 1004 -28 996 -36 988 -36 c 2 + 855 -36 l 2 + 847 -36 840 -28 840 -20 c 2 + 840 288 l 1 + 455 -32 l 2 + 445 -40 430 -33 430 -20 c 2 + 430 288 l 1 + 46 -32 l 2 + 36 -40 20 -33 20 -20 c 2 + 20 635 l 2 + 20 643 29 650 37 650 c 0 + 40 650 43 648 46 646 c 2 + 430 327 l 1 + 430 635 l 2 + 430 643 437 650 445 650 c 0 + 448 650 452 648 455 646 c 2 + 840 327 l 1 + 840 635 l 2 + 840 643 847 650 855 650 c 2 + 988 650 l 2 +EndSplineSet +EndChar + +StartChar: uni23EE +Encoding: 9198 9198 8 +Width: 1024 +VWidth: 0 +Flags: W +VStem: 20 164<-36 288 327 650> +LayerCount: 2 +Fore +SplineSet +36 650 m 2 + 169 650 l 2 + 177 650 184 643 184 635 c 2 + 184 327 l 1 + 569 646 l 2 + 572 648 576 650 579 650 c 0 + 587 650 594 643 594 635 c 2 + 594 327 l 1 + 978 646 l 2 + 981 648 984 650 987 650 c 0 + 995 650 1004 643 1004 635 c 2 + 1004 -20 l 2 + 1004 -33 988 -40 978 -32 c 2 + 594 288 l 1 + 594 -20 l 2 + 594 -33 579 -40 569 -32 c 2 + 184 288 l 1 + 184 -20 l 2 + 184 -28 177 -36 169 -36 c 2 + 36 -36 l 2 + 28 -36 20 -28 20 -20 c 2 + 20 635 l 2 + 20 643 28 650 36 650 c 2 +EndSplineSet +EndChar + +StartChar: uni23EF +Encoding: 9199 9199 9 +Width: 1024 +VWidth: 0 +Flags: W +VStem: 561 164<-36 650> 840 164<-36 650> +LayerCount: 2 +Fore +SplineSet +36 650 m 0 + 40 650 43 648 46 646 c 2 + 439 319 l 2 + 446 313 446 301 439 295 c 2 + 46 -32 l 2 + 36 -40 20 -33 20 -20 c 2 + 20 635 l 2 + 20 643 28 650 36 650 c 0 +577 650 m 2 + 710 650 l 2 + 718 650 725 643 725 635 c 2 + 725 -20 l 2 + 725 -28 718 -36 710 -36 c 2 + 577 -36 l 2 + 569 -36 561 -28 561 -20 c 2 + 561 635 l 2 + 561 643 569 650 577 650 c 2 +855 650 m 2 + 988 650 l 2 + 996 650 1004 643 1004 635 c 2 + 1004 -20 l 2 + 1004 -28 996 -36 988 -36 c 2 + 855 -36 l 2 + 847 -36 840 -28 840 -20 c 2 + 840 635 l 2 + 840 643 847 650 855 650 c 2 +EndSplineSet +EndChar + +StartChar: uni2B60 +Encoding: 11104 11104 10 +Width: 1024 +Flags: W +HStem: 225 164<445 1004> +LayerCount: 2 +Fore +SplineSet +430 650 m 0 + 438 650 445 643 445 635 c 2 + 445 404 l 2 + 445 396 453 389 461 389 c 2 + 988 389 l 2 + 996 389 1004 382 1004 374 c 2 + 1004 241 l 2 + 1004 233 996 225 988 225 c 2 + 461 225 l 2 + 453 225 445 218 445 210 c 2 + 445 -20 l 2 + 445 -33 430 -40 420 -32 c 2 + 26 295 l 2 + 19 301 19 313 26 319 c 2 + 420 646 l 2 + 423 648 426 650 430 650 c 0 +EndSplineSet +EndChar + +StartChar: uni2B62 +Encoding: 11106 11106 11 +Width: 1024 +Flags: W +HStem: 225 164<20 579> +LayerCount: 2 +Fore +SplineSet +594 650 m 0 + 598 650 601 648 604 646 c 2 + 997 319 l 2 + 1004 313 1004 301 997 295 c 2 + 604 -32 l 2 + 594 -40 579 -33 579 -20 c 2 + 579 210 l 2 + 579 218 571 225 563 225 c 2 + 36 225 l 2 + 28 225 20 233 20 241 c 2 + 20 374 l 2 + 20 382 28 389 36 389 c 2 + 563 389 l 2 + 571 389 579 396 579 404 c 2 + 579 635 l 2 + 579 643 586 650 594 650 c 0 +EndSplineSet +EndChar + +StartChar: u1FA9F +Encoding: 129695 129695 12 +Width: 1024 +Flags: W +HStem: -184 450<20 471 553 1004> 348 451<20 471 553 1004> +VStem: 20 451<-184 266 348 799> 553 451<-184 266 348 799> +LayerCount: 2 +Fore +SplineSet +560 266 m 2 + 996 266 l 2 + 1000 266 1004 263 1004 259 c 2 + 1004 -177 l 2 + 1004 -181 1000 -184 996 -184 c 2 + 560 -184 l 2 + 556 -184 553 -181 553 -177 c 2 + 553 259 l 2 + 553 263 556 266 560 266 c 2 +28 266 m 2 + 464 266 l 2 + 468 266 471 263 471 259 c 2 + 471 -177 l 2 + 471 -181 468 -184 464 -184 c 2 + 28 -184 l 2 + 24 -184 20 -181 20 -177 c 2 + 20 259 l 2 + 20 263 24 266 28 266 c 2 +560 799 m 2 + 996 799 l 2 + 1000 799 1004 796 1004 792 c 2 + 1004 355 l 2 + 1004 351 1000 348 996 348 c 2 + 560 348 l 2 + 556 348 553 351 553 355 c 2 + 553 792 l 2 + 553 796 556 799 560 799 c 2 +28 799 m 2 + 464 799 l 2 + 468 799 471 796 471 792 c 2 + 471 355 l 2 + 471 351 468 348 464 348 c 2 + 28 348 l 2 + 24 348 20 351 20 355 c 2 + 20 792 l 2 + 20 796 24 799 28 799 c 2 +EndSplineSet +EndChar + +StartChar: equivalence +Encoding: 8801 8801 13 +Width: 1024 +Flags: W +HStem: 47 83<169 855> 266 82<169 855> 484 83<169 855> +CounterMasks: 1 e0 +LayerCount: 2 +Fore +SplineSet +855 333 m 2 + 855 282 l 2 + 855 274 848 266 840 266 c 2 + 184 266 l 2 + 176 266 169 274 169 282 c 2 + 169 333 l 2 + 169 341 176 348 184 348 c 2 + 840 348 l 2 + 848 348 855 341 855 333 c 2 +855 115 m 2 + 855 62 l 2 + 855 54 848 47 840 47 c 2 + 184 47 l 2 + 176 47 169 54 169 62 c 2 + 169 115 l 2 + 169 123 176 130 184 130 c 2 + 840 130 l 2 + 848 130 855 123 855 115 c 2 +855 552 m 2 + 855 500 l 2 + 855 492 848 484 840 484 c 2 + 184 484 l 2 + 176 484 169 492 169 500 c 2 + 169 552 l 2 + 169 560 176 567 184 567 c 2 + 840 567 l 2 + 848 567 855 560 855 552 c 2 +EndSplineSet +EndChar + +StartChar: uni2B70 +Encoding: 11120 11120 14 +Width: 1024 +Flags: W +HStem: 225 164<594 1004> +VStem: 20 164<-36 288 327 650> +LayerCount: 2 +Fore +SplineSet +36 650 m 2 + 169 650 l 2 + 177 650 184 643 184 635 c 2 + 184 327 l 1 + 569 646 l 2 + 572 648 576 650 579 650 c 0 + 587 650 594 643 594 635 c 2 + 594 404 l 2 + 594 396 601 389 609 389 c 2 + 988 389 l 2 + 996 389 1004 382 1004 374 c 2 + 1004 241 l 2 + 1004 233 996 225 988 225 c 2 + 609 225 l 2 + 601 225 594 218 594 210 c 2 + 594 -20 l 2 + 594 -33 579 -40 569 -32 c 2 + 184 288 l 1 + 184 -20 l 2 + 184 -28 177 -36 169 -36 c 2 + 36 -36 l 2 + 28 -36 20 -28 20 -20 c 2 + 20 635 l 2 + 20 643 28 650 36 650 c 2 +EndSplineSet +EndChar + +StartChar: uni2B72 +Encoding: 11122 11122 15 +Width: 1024 +Flags: W +HStem: 225 164<20 430> +VStem: 840 164<-36 288 327 650> +LayerCount: 2 +Fore +SplineSet +988 650 m 2 + 996 650 1004 643 1004 635 c 2 + 1004 -20 l 2 + 1004 -28 996 -36 988 -36 c 2 + 855 -36 l 2 + 847 -36 840 -28 840 -20 c 2 + 840 288 l 1 + 455 -32 l 2 + 445 -40 430 -33 430 -20 c 2 + 430 210 l 2 + 430 218 423 225 415 225 c 2 + 36 225 l 2 + 28 225 20 233 20 241 c 2 + 20 374 l 2 + 20 382 28 389 36 389 c 2 + 415 389 l 2 + 423 389 430 396 430 404 c 2 + 430 635 l 2 + 430 643 437 650 445 650 c 0 + 448 650 452 648 455 646 c 2 + 840 327 l 1 + 840 635 l 2 + 840 643 847 650 855 650 c 2 + 988 650 l 2 +EndSplineSet +EndChar + +StartChar: uni232B +Encoding: 9003 9003 16 +Width: 1024 +Flags: W +HStem: 495 155<546 601.75 822.25 877> +LayerCount: 2 +Fore +SplineSet +430 650 m 2 + 988 650 l 2 + 996 650 1004 643 1004 635 c 2 + 1004 374 l 1 + 1004 241 l 1 + 1004 -20 l 2 + 1004 -28 996 -36 988 -36 c 2 + 430 -36 l 2 + 427 -36 423 -34 420 -32 c 2 + 26 295 l 2 + 19 301 19 313 26 319 c 2 + 420 646 l 2 + 423 648 426 650 430 650 c 2 +848 495 m 0 + 844 495 841 493 838 490 c 2 + 712 365 l 1 + 586 490 l 2 + 583 493 579 495 575 495 c 0 + 571 495 567 493 564 490 c 2 + 528 455 l 2 + 522 449 522 439 528 433 c 2 + 654 307 l 1 + 528 181 l 2 + 522 175 522 166 528 160 c 2 + 564 124 l 2 + 570 118 580 118 586 124 c 2 + 712 250 l 1 + 838 124 l 2 + 844 118 853 118 859 124 c 2 + 895 160 l 2 + 901 166 901 175 895 181 c 2 + 769 307 l 1 + 895 433 l 2 + 901 439 901 449 895 455 c 2 + 859 490 l 2 + 856 493 852 495 848 495 c 0 +EndSplineSet +EndChar + +StartChar: uni2326 +Encoding: 8998 8998 17 +Width: 1024 +Flags: W +HStem: 495 155<147 201.75 421.375 478> +LayerCount: 2 +Fore +SplineSet +594 650 m 2 + 598 650 601 648 604 646 c 2 + 997 319 l 2 + 1004 313 1004 301 997 295 c 2 + 604 -32 l 2 + 601 -34 597 -36 594 -36 c 2 + 36 -36 l 2 + 28 -36 20 -28 20 -20 c 2 + 20 241 l 1 + 20 374 l 1 + 20 635 l 2 + 20 643 28 650 36 650 c 2 + 594 650 l 2 +176 495 m 0 + 172 495 168 493 165 490 c 2 + 129 455 l 2 + 123 449 123 439 129 433 c 2 + 255 307 l 1 + 129 181 l 2 + 123 175 123 166 129 160 c 2 + 165 124 l 2 + 171 118 180 118 186 124 c 2 + 312 250 l 1 + 437 124 l 2 + 443 118 454 118 460 124 c 2 + 496 160 l 2 + 502 166 502 175 496 181 c 2 + 370 307 l 1 + 496 433 l 2 + 502 439 502 449 496 455 c 2 + 460 490 l 2 + 457 493 453 495 449 495 c 0 + 445 495 440 493 437 490 c 2 + 312 365 l 1 + 186 490 l 2 + 183 493 180 495 176 495 c 0 +EndSplineSet +EndChar + +StartChar: arrowleft +Encoding: 8592 8592 18 +Width: 1024 +Flags: W +HStem: 225 164<445 1004> +LayerCount: 2 +Fore +SplineSet +430 650 m 0 + 438 650 445 643 445 635 c 2 + 445 404 l 2 + 445 396 453 389 461 389 c 2 + 988 389 l 2 + 996 389 1004 382 1004 374 c 2 + 1004 241 l 2 + 1004 233 996 225 988 225 c 2 + 461 225 l 2 + 453 225 445 218 445 210 c 2 + 445 -20 l 2 + 445 -33 430 -40 420 -32 c 2 + 26 295 l 2 + 19 301 19 313 26 319 c 2 + 420 646 l 2 + 423 648 426 650 430 650 c 0 +EndSplineSet +EndChar + +StartChar: arrowup +Encoding: 8593 8593 19 +Width: 1024 +Flags: W +VStem: 430 164<-184 374> +LayerCount: 2 +Fore +SplineSet +855 389 m 0 + 855 381 848 374 840 374 c 2 + 609 374 l 2 + 601 374 594 366 594 358 c 2 + 594 -169 l 2 + 594 -177 587 -184 579 -184 c 2 + 445 -184 l 2 + 437 -184 430 -177 430 -169 c 2 + 430 358 l 2 + 430 366 423 374 415 374 c 2 + 184 374 l 2 + 171 374 165 389 173 399 c 2 + 500 793 l 2 + 506 800 518 800 524 793 c 2 + 851 399 l 2 + 853 396 855 393 855 389 c 0 +EndSplineSet +EndChar + +StartChar: arrowright +Encoding: 8594 8594 20 +Width: 1024 +Flags: W +HStem: 225 164<20 579> +LayerCount: 2 +Fore +SplineSet +594 650 m 0 + 598 650 601 648 604 646 c 2 + 997 319 l 2 + 1004 313 1004 301 997 295 c 2 + 604 -32 l 2 + 594 -40 579 -33 579 -20 c 2 + 579 210 l 2 + 579 218 571 225 563 225 c 2 + 36 225 l 2 + 28 225 20 233 20 241 c 2 + 20 374 l 2 + 20 382 28 389 36 389 c 2 + 563 389 l 2 + 571 389 579 396 579 404 c 2 + 579 635 l 2 + 579 643 586 650 594 650 c 0 +EndSplineSet +EndChar + +StartChar: arrowdown +Encoding: 8595 8595 21 +Width: 1024 +Flags: W +VStem: 430 164<241 799> +LayerCount: 2 +Fore +SplineSet +169 225 m 0 + 169 233 176 241 184 241 c 2 + 415 241 l 2 + 423 241 430 248 430 256 c 2 + 430 783 l 2 + 430 791 437 799 445 799 c 2 + 579 799 l 2 + 587 799 594 791 594 783 c 2 + 594 256 l 2 + 594 248 601 241 609 241 c 2 + 840 241 l 2 + 853 241 859 225 851 215 c 2 + 524 -178 l 2 + 518 -185 506 -185 500 -178 c 2 + 172 215 l 2 + 170 218 169 221 169 225 c 0 +EndSplineSet +EndChar + +StartChar: uni2B82 +Encoding: 11138 11138 22 +Width: 1024 +Flags: W +HStem: 85 164<364 620 854 1004> 366 163<20 660> +LayerCount: 2 +Fore +SplineSet +676 750 m 0 + 680 750 683 748 686 746 c 2 + 997 460 l 2 + 1004 454 1004 442 997 436 c 2 + 686 150 l 2 + 676 142 660 149 660 162 c 2 + 660 350 l 2 + 660 358 653 366 645 366 c 2 + 36 366 l 2 + 28 366 20 373 20 381 c 2 + 20 514 l 2 + 20 522 28 529 36 529 c 2 + 645 529 l 2 + 653 529 660 537 660 545 c 2 + 660 734 l 2 + 660 742 668 750 676 750 c 0 +185 325 m 1 + 244 325 305 325 364 325 c 1 + 364 264 l 2 + 364 256 371 249 379 249 c 2 + 620 249 l 1 + 620 217 620 184 621 152 c 0 + 625 124 652 103 680 105 c 0 + 693 106 706 113 716 122 c 0 + 736 140 756 159 776 177 c 0 + 802 201 828 225 854 249 c 1 + 988 249 l 2 + 996 249 1004 241 1004 233 c 2 + 1004 100 l 2 + 1004 92 996 85 988 85 c 2 + 379 85 l 2 + 371 85 364 78 364 70 c 2 + 364 -120 l 2 + 364 -133 348 -140 338 -132 c 2 + 26 155 l 2 + 19 161 19 172 26 178 c 2 + 185 325 l 1 +EndSplineSet +EndChar + +StartChar: uni2BA0 +Encoding: 11168 11168 23 +Width: 1024 +HStem: 225 164<445 840> +VStem: 840 164<389 799> +LayerCount: 2 +Fore +SplineSet +855 799 m 2 + 988 799 l 2 + 996 799 1004 791 1004 783 c 2 + 1004 240 l 2 + 1004 232 996 225 988 225 c 2 + 461 225 l 2 + 453 225 445 218 445 210 c 2 + 445 -20 l 2 + 445 -33 430 -40 420 -32 c 2 + 26 295 l 2 + 18 301 18 313 26 319 c 2 + 420 646 l 2 + 423 648 426 650 430 650 c 0 + 438 650 445 642 445 634 c 2 + 445 404 l 2 + 445 396 453 389 461 389 c 2 + 824 389 l 2 + 832 389 840 396 840 404 c 2 + 840 783 l 2 + 840 791 847 799 855 799 c 2 +EndSplineSet +EndChar + +StartChar: uni2BC7 +Encoding: 11207 11207 24 +Width: 1024 +LayerCount: 2 +Fore +SplineSet +793 -34 m 2 + 215 294 l 2 + 210 297 208 301 208 307 c 0 + 208 313 210 317 215 320 c 2 + 793 648 l 2 + 798 651 804 651 809 648 c 0 + 814 645 816 639 816 634 c 2 + 816 -20 l 2 + 816 -25 814 -31 809 -34 c 0 + 804 -37 798 -37 793 -34 c 2 +EndSplineSet +EndChar +EndChars +EndSplineFont diff --git a/icon_font/icon.sfd b/icon_font/icon.sfd deleted file mode 100644 index 5e06aea..0000000 --- a/icon_font/icon.sfd +++ /dev/null @@ -1,742 +0,0 @@ -SplineFontDB: 3.2 -FontName: Icon -FullName: Icon -FamilyName: Icon -Weight: Regular -Copyright: Copyright (c) 2018 Unrud -UComments: "2018-8-29: Created with FontForge (http://fontforge.org)" -Version: 001.000 -ItalicAngle: 0 -UnderlinePosition: -100 -UnderlineWidth: 50 -Ascent: 800 -Descent: 200 -InvalidEm: 0 -LayerCount: 2 -Layer: 0 0 "Back" 1 -Layer: 1 0 "Zeichen" 0 -XUID: [1021 32 396054688 5708680] -FSType: 0 -OS2Version: 0 -OS2_WeightWidthSlopeOnly: 0 -OS2_UseTypoMetrics: 1 -CreationTime: 1535548659 -ModificationTime: 1597866573 -PfmFamily: 17 -TTFWeight: 400 -TTFWidth: 5 -LineGap: 90 -VLineGap: 0 -OS2TypoAscent: 0 -OS2TypoAOffset: 1 -OS2TypoDescent: 0 -OS2TypoDOffset: 1 -OS2TypoLinegap: 90 -OS2WinAscent: 0 -OS2WinAOffset: 1 -OS2WinDescent: 0 -OS2WinDOffset: 1 -HheadAscent: 0 -HheadAOffset: 1 -HheadDescent: 0 -HheadDOffset: 1 -OS2Vendor: 'PfEd' -MarkAttachClasses: 1 -DEI: 91125 -LangName: 1033 -Encoding: UnicodeFull -Compacted: 1 -UnicodeInterp: none -NameList: AGL For New Fonts -DisplaySize: -48 -AntiAlias: 1 -FitToEm: 0 -WinInfo: 0 35 14 -BeginPrivate: 0 -EndPrivate -TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144 -BeginChars: 1114112 14 - -StartChar: uni2328 -Encoding: 9000 9000 0 -Width: 1000 -VWidth: 0 -HStem: 10 80<250 750> 200 60<285 395 445 555 605 715> 370 50<125 235 285 395 445 555 605 715 765 875> 530 60<125 235 285 395 445 555 605 715 765 875> -VStem: 20 105<260 370 420 530> 235 50<260 370 420 530> 395 50<260 370 420 530> 555 50<260 370 420 530> 715 50<260 370 420 530> 750 230<90 200> 875 105<260 370 420 530> -LayerCount: 2 -Fore -SplineSet -70 590 m 2xff20 - 930 590 l 2 - 957.700195312 590 980 567.700195312 980 540 c 2 - 980 60 l 2 - 980 32.2998046875 957.700195312 10 930 10 c 2 - 70 10 l 2 - 42.2998046875 10 20 32.2998046875 20 60 c 2 - 20 540 l 2 - 20 567.700195312 42.2998046875 590 70 590 c 2xff20 -135 530 m 2 - 129.459960938 530 125 525.540039062 125 520 c 2 - 125 430 l 2 - 125 424.459960938 129.459960938 420 135 420 c 2 - 225 420 l 2 - 230.540039062 420 235 424.459960938 235 430 c 2 - 235 520 l 2 - 235 525.540039062 230.540039062 530 225 530 c 2 - 135 530 l 2 -295 530 m 2 - 289.459960938 530 285 525.540039062 285 520 c 2 - 285 430 l 2 - 285 424.459960938 289.459960938 420 295 420 c 2 - 385 420 l 2 - 390.540039062 420 395 424.459960938 395 430 c 2 - 395 520 l 2 - 395 525.540039062 390.540039062 530 385 530 c 2 - 295 530 l 2 -455 530 m 2 - 449.459960938 530 445 525.540039062 445 520 c 2 - 445 430 l 2 - 445 424.459960938 449.459960938 420 455 420 c 2 - 545 420 l 2 - 550.540039062 420 555 424.459960938 555 430 c 2 - 555 520 l 2 - 555 525.540039062 550.540039062 530 545 530 c 2 - 455 530 l 2 -615 530 m 2 - 609.459960938 530 605 525.540039062 605 520 c 2 - 605 430 l 2 - 605 424.459960938 609.459960938 420 615 420 c 2 - 705 420 l 2 - 710.540039062 420 715 424.459960938 715 430 c 2 - 715 520 l 2xff80 - 715 525.540039062 710.540039062 530 705 530 c 2 - 615 530 l 2 -775 530 m 2 - 769.459960938 530 765 525.540039062 765 520 c 2 - 765 430 l 2 - 765 424.459960938 769.459960938 420 775 420 c 2 - 865 420 l 2 - 870.540039062 420 875 424.459960938 875 430 c 2 - 875 520 l 2xffa0 - 875 525.540039062 870.540039062 530 865 530 c 2 - 775 530 l 2 -135 370 m 2 - 129.459960938 370 125 365.540039062 125 360 c 2 - 125 270 l 2 - 125 264.459960938 129.459960938 260 135 260 c 2 - 225 260 l 2 - 230.540039062 260 235 264.459960938 235 270 c 2 - 235 360 l 2 - 235 365.540039062 230.540039062 370 225 370 c 2 - 135 370 l 2 -295 370 m 2 - 289.459960938 370 285 365.540039062 285 360 c 2 - 285 270 l 2 - 285 264.459960938 289.459960938 260 295 260 c 2 - 385 260 l 2 - 390.540039062 260 395 264.459960938 395 270 c 2 - 395 360 l 2 - 395 365.540039062 390.540039062 370 385 370 c 2 - 295 370 l 2 -455 370 m 2 - 449.459960938 370 445 365.540039062 445 360 c 2 - 445 270 l 2 - 445 264.459960938 449.459960938 260 455 260 c 2 - 545 260 l 2 - 550.540039062 260 555 264.459960938 555 270 c 2 - 555 360 l 2 - 555 365.540039062 550.540039062 370 545 370 c 2 - 455 370 l 2 -615 370 m 2 - 609.459960938 370 605 365.540039062 605 360 c 2 - 605 270 l 2 - 605 264.459960938 609.459960938 260 615 260 c 2 - 705 260 l 2 - 710.540039062 260 715 264.459960938 715 270 c 2 - 715 360 l 2 - 715 365.540039062 710.540039062 370 705 370 c 2 - 615 370 l 2 -775 370 m 2 - 769.459960938 370 765 365.540039062 765 360 c 2 - 765 270 l 2 - 765 264.459960938 769.459960938 260 775 260 c 2 - 865 260 l 2 - 870.540039062 260 875 264.459960938 875 270 c 2 - 875 360 l 2 - 875 365.540039062 870.540039062 370 865 370 c 2 - 775 370 l 2 -260 200 m 2 - 254.459960938 200 250 195.540039062 250 190 c 2 - 250 100 l 2 - 250 94.4599609375 254.459960938 90 260 90 c 2 - 740 90 l 2 - 745.540039062 90 750 94.4599609375 750 100 c 2 - 750 190 l 2xff40 - 750 195.540039062 745.540039062 200 740 200 c 2 - 260 200 l 2 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni2922 -Encoding: 10530 10530 1 -Width: 1000 -VWidth: 0 -HStem: -90.0059 80.1387<246.65 410.016> 609.867 80.1387<589.938 753.304> -VStem: 109.941 80.1387<46.7031 210.068> 809.874 80.1387<389.932 553.297> -LayerCount: 2 -Fore -SplineSet -599.938476562 690.005859375 m 2 - 880.030273438 690.005859375 l 2 - 885.454101562 690.005859375 889.83203125 685.727539062 890.012695312 680.34765625 c 2 - 890.012695312 399.931640625 l 2 - 890.012695312 394.391601562 885.552734375 389.931640625 880.012695312 389.931640625 c 2 - 819.874023438 389.931640625 l 2 - 814.333984375 389.931640625 809.874023438 394.391601562 809.874023438 399.931640625 c 2 - 809.874023438 553.296875 l 1 - 635.295898438 378.71484375 l 2 - 631.377929688 374.799804688 625.073242188 374.80078125 621.157226562 378.716796875 c 2 - 578.723632812 421.150390625 l 2 - 574.806640625 425.067382812 574.805664062 431.372070312 578.721679688 435.2890625 c 2 - 578.723632812 435.291015625 l 1 - 753.303710938 609.8671875 l 1 - 599.543945312 609.8671875 l 2 - 594.189453125 610.075195312 589.938476562 614.44140625 589.938476562 619.84765625 c 2 - 589.938476562 680.005859375 l 2 - 589.938476562 685.545898438 594.3984375 690.005859375 599.938476562 690.005859375 c 2 -400.015625 -90.005859375 m 2 - 119.923828125 -90.005859375 l 2 - 114.499023438 -90.005859375 110.122070312 -85.7275390625 109.94140625 -80.34765625 c 2 - 109.94140625 200.068359375 l 2 - 109.94140625 205.608398438 114.401367188 210.068359375 119.94140625 210.068359375 c 2 - 180.080078125 210.068359375 l 2 - 185.620117188 210.068359375 190.080078125 205.608398438 190.080078125 200.068359375 c 2 - 190.080078125 46.703125 l 1 - 364.658203125 221.28515625 l 2 - 368.575195312 225.200195312 374.879882812 225.19921875 378.796875 221.283203125 c 2 - 421.23046875 178.849609375 l 2 - 425.146484375 174.932617188 425.147460938 168.627929688 421.232421875 164.7109375 c 2 - 421.23046875 164.708984375 l 1 - 246.650390625 -9.8671875 l 1 - 400.41015625 -9.8671875 l 2 - 405.763671875 -10.0751953125 410.015625 -14.44140625 410.015625 -19.84765625 c 2 - 410.015625 -80.005859375 l 2 - 410.015625 -85.5458984375 405.555664062 -90.005859375 400.015625 -90.005859375 c 2 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni27A3 -Encoding: 10147 10147 2 -Width: 1000 -VWidth: 0 -Flags: HM -LayerCount: 2 -Fore -SplineSet -33.16015625 636.0625 m 0 - 33.1669921875 636.0625 33.1728515625 636.061523438 33.1796875 636.060546875 c 0 - 33.619140625 636.0546875 34.056640625 636.0234375 34.4921875 635.978515625 c 0 - 34.6171875 635.96484375 34.8203125 635.939453125 34.9453125 635.921875 c 0 - 35.2578125 635.880859375 35.568359375 635.829101562 35.876953125 635.767578125 c 0 - 35.9912109375 635.74609375 36.1767578125 635.70703125 36.291015625 635.681640625 c 0 - 36.7275390625 635.583007812 37.1611328125 635.470703125 37.587890625 635.33203125 c 2 - 969.647460938 333.731445312 l 2 - 969.954101562 333.631835938 970.444335938 333.452148438 970.743164062 333.329101562 c 0 - 971.173828125 333.151367188 971.590820312 332.94921875 971.999023438 332.735351562 c 0 - 972.079101562 332.692382812 972.208007812 332.62109375 972.288085938 332.577148438 c 0 - 972.6015625 332.403320312 972.907226562 332.21875 973.206054688 332.024414062 c 0 - 973.291015625 331.969726562 973.428710938 331.879882812 973.512695312 331.823242188 c 0 - 973.85546875 331.587890625 974.1875 331.340820312 974.508789062 331.079101562 c 0 - 974.534179688 331.05859375 974.576171875 331.025390625 974.602539062 331.004882812 c 0 - 974.962890625 330.706054688 975.305664062 330.389648438 975.635742188 330.059570312 c 0 - 975.685546875 330.0078125 975.767578125 329.922851562 975.817382812 329.870117188 c 0 - 976.078125 329.599609375 976.329101562 329.317382812 976.569335938 329.028320312 c 0 - 976.62890625 328.95703125 976.725585938 328.83984375 976.784179688 328.766601562 c 0 - 977.045898438 328.435546875 977.295898438 328.095703125 977.530273438 327.743164062 c 0 - 977.545898438 327.720703125 977.571289062 327.68359375 977.586914062 327.661132812 c 0 - 977.844726562 327.266601562 978.081054688 326.859375 978.301757812 326.440429688 c 0 - 978.329101562 326.385742188 978.374023438 326.294921875 978.401367188 326.239257812 c 0 - 978.579101562 325.889648438 978.743164062 325.53125 978.893554688 325.166992188 c 0 - 978.924804688 325.090820312 978.974609375 324.965820312 979.004882812 324.887695312 c 0 - 979.165039062 324.475585938 979.310546875 324.056640625 979.434570312 323.627929688 c 0 - 979.4375 323.62109375 979.442382812 323.609375 979.4453125 323.6015625 c 0 - 979.446289062 323.59765625 979.447265625 323.596679688 979.448242188 323.59375 c 0 - 979.58203125 323.125976562 979.693359375 322.649414062 979.782226562 322.1640625 c 0 - 979.786132812 322.137695312 979.79296875 322.095703125 979.796875 322.069335938 c 0 - 979.873046875 321.640625 979.92578125 321.206054688 979.96484375 320.768554688 c 0 - 979.970703125 320.698242188 979.979492188 320.5859375 979.983398438 320.515625 c 0 - 979.99609375 320.34375 980.014648438 320.171875 980.022460938 319.998046875 c 2 - 980 319.998046875 l 2 - 980.005859375 319.84765625 980.009765625 319.602539062 980.009765625 319.451171875 c 0 - 980.009765625 313.670898438 975.60546875 307.362304688 970.1796875 305.369140625 c 2 - 38.11328125 -35.15625 l 2 - 36.7392578125 -35.6572265625 34.4365234375 -36.0634765625 32.9736328125 -36.0634765625 c 0 - 24.6923828125 -36.0634765625 17.9716796875 -29.3427734375 17.9716796875 -21.0625 c 0 - 17.9716796875 -19.0283203125 18.7373046875 -15.9140625 19.6796875 -14.111328125 c 2 - 194.083984375 319.01171875 l 1 - 193.5 319.998046875 l 1 - 193.498046875 319.998046875 l 1 - 20.056640625 613.4296875 l 2 - 19.99609375 613.537109375 19.9013671875 613.711914062 19.84375 613.8203125 c 0 - 19.638671875 614.190429688 19.451171875 614.564453125 19.28125 614.939453125 c 0 - 19.2705078125 614.962890625 19.2529296875 615.000976562 19.2421875 615.0234375 c 0 - 17.513671875 618.904296875 17.6025390625 623.040039062 19.037109375 626.611328125 c 0 - 19.046875 626.637695312 19.0634765625 626.6796875 19.07421875 626.705078125 c 0 - 19.244140625 627.120117188 19.431640625 627.526367188 19.63671875 627.923828125 c 0 - 19.67578125 628 19.740234375 628.12109375 19.78125 628.1953125 c 0 - 19.97265625 628.549804688 20.177734375 628.896484375 20.396484375 629.234375 c 0 - 20.4169921875 629.267578125 20.4501953125 629.319335938 20.470703125 629.3515625 c 0 - 20.7236328125 629.735351562 20.998046875 630.104492188 21.28515625 630.462890625 c 0 - 21.314453125 630.5 21.3623046875 630.559570312 21.392578125 630.595703125 c 0 - 23.8017578125 633.541992188 27.3330078125 635.6015625 31.525390625 635.98828125 c 0 - 31.595703125 635.99609375 31.708984375 636.005859375 31.779296875 636.01171875 c 0 - 32.2333984375 636.045898438 32.69140625 636.069335938 33.16015625 636.0625 c 0 -226.22265625 319.998046875 m 2 - 226.228515625 319.842773438 226.233398438 319.58984375 226.233398438 319.43359375 c 0 - 226.233398438 317.405273438 225.47265625 314.298828125 224.53515625 312.5 c 2 - 64.236328125 6.322265625 l 1 - 918.96484375 318.59765625 l 1 - 914.63671875 319.998046875 l 1 - 226.22265625 319.998046875 l 2 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni21BB -Encoding: 8635 8635 3 -Width: 1000 -VWidth: 0 -HStem: -11.9004 80.0049<410.621 596.596> 531.894 80.0068<403.658 587.978> -VStem: 188.142 80.0029<203.446 389.421> -LayerCount: 2 -Fore -SplineSet -487.42578125 611.645507812 m 0 - 490.903320312 611.786132812 496.55078125 611.900390625 500.032226562 611.900390625 c 0 - 600.807617188 611.900390625 722.643554688 540.5859375 771.986328125 452.717773438 c 1 - 771.994140625 452.721679688 l 1 - 813.62109375 600.739257812 l 2 - 815.270507812 605.836914062 820.623046875 608.74609375 825.828125 607.282226562 c 2 - 883.740234375 590.997070312 l 2 - 889.073242188 589.497070312 892.16015625 583.99609375 890.66015625 578.663085938 c 2 - 814.83203125 309.032226562 l 2 - 813.36328125 303.809570312 808.059570312 300.752929688 802.83203125 302.036132812 c 2 - 532.88671875 377.952148438 l 2 - 527.553710938 379.451171875 524.46875 384.951171875 525.96875 390.284179688 c 2 - 542.248046875 448.178710938 l 2 - 543.748046875 453.51171875 549.249023438 456.595703125 554.58203125 455.096679688 c 2 - 702.212890625 413.579101562 l 1 - 665.522460938 478.888671875 574.947265625 531.893554688 500.036132812 531.893554688 c 0 - 466.584960938 531.893554688 415.756835938 518.611328125 386.58203125 502.245117188 c 0 - 321.205078125 465.568359375 268.14453125 374.961914062 268.14453125 300 c 0 - 268.14453125 266.545898438 281.428710938 215.715820312 297.796875 186.540039062 c 0 - 334.473632812 121.163085938 425.079101562 68.1044921875 500.041015625 68.1044921875 c 0 - 533.495117188 68.1044921875 584.325195312 81.388671875 613.501953125 97.7568359375 c 1 - 652.64453125 27.9814453125 l 1 - 613.403320312 5.966796875 545.037109375 -11.900390625 500.041992188 -11.900390625 c 0 - 399.217773438 -11.900390625 277.353515625 59.46484375 228.0234375 147.397460938 c 0 - 206.008789062 186.638671875 188.141601562 255.004882812 188.141601562 300 c 0 - 188.141601562 400.823242188 259.505859375 522.6875 347.4375 572.018554688 c 0 - 383.451171875 592.221679688 446.166015625 609.974609375 487.42578125 611.645507812 c 0 -EndSplineSet -Validated: 1 -EndChar - -StartChar: u1F50A -Encoding: 128266 128266 4 -Width: 1000 -VWidth: 0 -HStem: 140.6 334.824<19.9492 262.816> -VStem: 19.9492 480.234<140.6 475.424> 516.504 97.4043<145.53 207.841 392.475 467.508> 538.93 104.705<166.83 432.826> 707.016 104.705<166.604 433.179> 875.107 104.535<168.541 431.014> -LayerCount: 2 -Fore -SplineSet -484.7421875 634.7890625 m 0xcc - 493.1953125 635.038085938 500.182617188 628.251953125 500.18359375 619.794921875 c 2 - 500.18359375 -19.80078125 l 2 - 500.176757812 -32.408203125 485.56640625 -39.3857421875 475.7578125 -31.46484375 c 2 - 262.81640625 140.599609375 l 1 - 34.94921875 140.599609375 l 2 - 26.6650390625 140.600585938 19.9501953125 147.315429688 19.94921875 155.599609375 c 2 - 19.94921875 460.423828125 l 2 - 19.9501953125 468.708007812 26.6650390625 475.422851562 34.94921875 475.423828125 c 2 - 282.64453125 475.423828125 l 1 - 475.7578125 631.466796875 l 2 - 478.30859375 633.526367188 481.46484375 634.693359375 484.7421875 634.7890625 c 0xcc -876.865234375 600.306640625 m 0 - 881.969726562 600.436523438 886.790039062 597.962890625 889.658203125 593.73828125 c 0 - 944.186523438 513.37109375 977.028320312 417.055664062 979.642578125 313.46875 c 0 - 979.64453125 313.34375 979.64453125 313.219726562 979.642578125 313.09375 c 2 - 979.642578125 287.02734375 l 2 - 979.64453125 286.90234375 979.64453125 286.776367188 979.642578125 286.650390625 c 0 - 977.047851562 183.834960938 944.668945312 88.185546875 890.869140625 8.18359375 c 0 - 886.895507812 2.27734375 879.317382812 -0.0224609375 872.73046875 2.677734375 c 2 - 802.31640625 31.578125 l 2 - 793.340820312 35.265625 790.171875 46.375 795.85546875 54.240234375 c 0 - 845.7421875 123.275390625 875.107421875 208.067382812 875.107421875 300.056640625 c 0 - 875.107421875 393.627929688 844.71875 479.7578125 793.2578125 549.423828125 c 0 - 787.33984375 557.43359375 790.725585938 568.879882812 800.048828125 572.380859375 c 2 - 871.978515625 599.36328125 l 2 - 873.54296875 599.948242188 875.1953125 600.265625 876.865234375 600.306640625 c 0 -739.732421875 548.775390625 m 0 - 745.377929688 548.819335938 750.568359375 545.688476562 753.166015625 540.67578125 c 0 - 790.561523438 468.57421875 811.720703125 386.693359375 811.720703125 300.056640625 c 0 - 811.720703125 215.825195312 791.721679688 136.088867188 756.244140625 65.466796875 c 0 - 752.723632812 58.4560546875 744.397460938 55.3427734375 737.140625 58.322265625 c 2 - 668.083984375 86.66796875 l 2 - 659.944335938 90.009765625 656.415039062 99.607421875 660.451171875 107.42578125 c 0 - 690.209960938 165.073242188 707.015625 230.493164062 707.015625 300.056640625 c 0 - 707.015625 372.688476562 688.69921875 440.814453125 656.44140625 500.275390625 c 0 - 652.09765625 508.279296875 655.829101562 518.276367188 664.35546875 521.4765625 c 2 - 734.580078125 547.814453125 l 2 - 736.227539062 548.434570312 737.971679688 548.760742188 739.732421875 548.775390625 c 0 -596.021484375 494.76953125 m 0 - 602.208007812 494.712890625 607.724609375 490.862304688 609.9140625 485.076171875 c 0 - 631.693359375 427.509765625 643.634765625 365.119140625 643.634765625 300.056640625 c 0xdc - 643.634765625 239.07421875 633.157226562 180.442382812 613.908203125 125.92578125 c 0 - 611.05078125 117.8359375 602.000976562 113.786132812 594.064453125 117.044921875 c 2 - 525.0078125 145.384765625 l 2 - 517.669921875 148.395507812 513.944335938 156.596679688 516.50390625 164.103515625 c 0xec - 531.033203125 206.7109375 538.9296875 252.407226562 538.9296875 300.056640625 c 0xdc - 538.9296875 352.326171875 529.440429688 402.254882812 512.103515625 448.314453125 c 0 - 509.180664062 456.075195312 513.108398438 464.735351562 520.873046875 467.6484375 c 2 - 590.6171875 493.80859375 l 2 - 592.34375 494.458007812 594.176757812 494.784179688 596.021484375 494.76953125 c 0 -EndSplineSet -Validated: 1 -EndChar - -StartChar: u1F509 -Encoding: 128265 128265 5 -Width: 1000 -VWidth: 0 -HStem: 140.6 334.824<19.9492 262.816> -VStem: 19.9492 480.234<140.6 475.424> 516.504 97.4043<145.53 207.841 392.475 467.508> 538.93 104.705<166.83 432.826> -LayerCount: 2 -Fore -SplineSet -484.7421875 634.7890625 m 0xc0 - 493.1953125 635.038085938 500.182617188 628.251953125 500.18359375 619.794921875 c 2 - 500.18359375 -19.80078125 l 2 - 500.176757812 -32.408203125 485.56640625 -39.3857421875 475.7578125 -31.46484375 c 2 - 262.81640625 140.599609375 l 1 - 34.94921875 140.599609375 l 2 - 26.6650390625 140.600585938 19.9501953125 147.315429688 19.94921875 155.599609375 c 2 - 19.94921875 460.423828125 l 2 - 19.9501953125 468.708007812 26.6650390625 475.422851562 34.94921875 475.423828125 c 2 - 282.64453125 475.423828125 l 1 - 475.7578125 631.466796875 l 2 - 478.30859375 633.526367188 481.46484375 634.693359375 484.7421875 634.7890625 c 0xc0 -596.021484375 494.76953125 m 0 - 602.208007812 494.712890625 607.724609375 490.862304688 609.9140625 485.076171875 c 0 - 631.693359375 427.509765625 643.634765625 365.119140625 643.634765625 300.056640625 c 0xd0 - 643.634765625 239.07421875 633.157226562 180.442382812 613.908203125 125.92578125 c 0 - 611.05078125 117.8359375 602.000976562 113.786132812 594.064453125 117.044921875 c 2 - 525.0078125 145.384765625 l 2 - 517.669921875 148.395507812 513.944335938 156.596679688 516.50390625 164.103515625 c 0xe0 - 531.033203125 206.7109375 538.9296875 252.407226562 538.9296875 300.056640625 c 0xd0 - 538.9296875 352.326171875 529.440429688 402.254882812 512.103515625 448.314453125 c 0 - 509.180664062 456.075195312 513.108398438 464.735351562 520.873046875 467.6484375 c 2 - 590.6171875 493.80859375 l 2 - 592.34375 494.458007812 594.176757812 494.784179688 596.021484375 494.76953125 c 0 -EndSplineSet -Validated: 1 -EndChar - -StartChar: u1F507 -Encoding: 128263 128263 6 -Width: 1000 -VWidth: 0 -HStem: 140.6 334.824<19.9492 262.816> -VStem: 19.9492 480.234<140.6 475.424> -LayerCount: 2 -Fore -SplineSet -484.7421875 634.7890625 m 0 - 493.1953125 635.038085938 500.182617188 628.251953125 500.18359375 619.794921875 c 2 - 500.18359375 -19.80078125 l 2 - 500.176757812 -32.408203125 485.56640625 -39.3857421875 475.7578125 -31.46484375 c 2 - 262.81640625 140.599609375 l 1 - 34.94921875 140.599609375 l 2 - 26.6650390625 140.600585938 19.9501953125 147.315429688 19.94921875 155.599609375 c 2 - 19.94921875 460.423828125 l 2 - 19.9501953125 468.708007812 26.6650390625 475.422851562 34.94921875 475.423828125 c 2 - 282.64453125 475.423828125 l 1 - 475.7578125 631.466796875 l 2 - 478.30859375 633.526367188 481.46484375 634.693359375 484.7421875 634.7890625 c 0 -662.77734375 483.349609375 m 0 - 666.83203125 483.409179688 670.739257812 481.825195312 673.607421875 478.95703125 c 2 - 796.154296875 356.41015625 l 1 - 918.69921875 478.95703125 l 2 - 924.557617188 484.815429688 934.057617188 484.815429688 939.916015625 478.95703125 c 2 - 975.11328125 443.759765625 l 2 - 980.971679688 437.901367188 980.971679688 428.403320312 975.11328125 422.544921875 c 2 - 852.572265625 299.998046875 l 1 - 975.11328125 177.451171875 l 2 - 980.96875 171.595703125 980.970703125 162.1015625 975.119140625 156.2421875 c 2 - 939.916015625 121.0390625 l 2 - 934.059570312 115.178710938 924.559570312 115.174804688 918.69921875 121.03125 c 2 - 796.154296875 243.583984375 l 1 - 673.607421875 121.03125 l 2 - 667.747070312 115.174804688 658.247070312 115.178710938 652.390625 121.0390625 c 2 - 617.1875 156.2421875 l 2 - 611.3359375 162.1015625 611.33984375 171.595703125 617.1953125 177.451171875 c 2 - 739.734375 299.998046875 l 1 - 617.1953125 422.544921875 l 2 - 611.336914062 428.403320312 611.336914062 437.901367188 617.1953125 443.759765625 c 2 - 652.390625 478.95703125 l 2 - 655.149414062 481.715820312 658.875976562 483.291992188 662.77734375 483.349609375 c 0 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni23ED -Encoding: 9197 9197 7 -Width: 1000 -VWidth: 0 -VStem: 819.777 160.131<-34.8008 280.725 319.277 634.795> -LayerCount: 2 -Fore -SplineSet -964.908203125 634.794921875 m 2 - 973.192382812 634.793945312 979.907226562 628.079101562 979.908203125 619.794921875 c 2 - 979.908203125 -19.80078125 l 2 - 979.907226562 -28.0849609375 973.192382812 -34.7998046875 964.908203125 -34.80078125 c 2 - 834.77734375 -34.80078125 l 2 - 826.493164062 -34.7998046875 819.778320312 -28.0849609375 819.77734375 -19.80078125 c 2 - 819.77734375 280.724609375 l 1 - 444.379882812 -31.46484375 l 2 - 434.571289062 -39.3857421875 419.958007812 -32.408203125 419.952148438 -19.80078125 c 2 - 419.952148438 280.740234375 l 1 - 44.533203125 -31.46484375 l 2 - 34.724609375 -39.3857421875 20.1142578125 -32.408203125 20.107421875 -19.80078125 c 2 - 20.107421875 619.794921875 l 2 - 20.1083984375 628.251953125 27.095703125 635.038085938 35.548828125 634.7890625 c 0 - 38.826171875 634.693359375 41.982421875 633.526367188 44.533203125 631.466796875 c 2 - 419.952148438 319.26171875 l 1 - 419.952148438 619.794921875 l 2 - 419.953125 628.251953125 426.94140625 635.038085938 435.395507812 634.7890625 c 0 - 438.672851562 634.693359375 441.828125 633.526367188 444.379882812 631.466796875 c 2 - 819.77734375 319.27734375 l 1 - 819.77734375 619.794921875 l 2 - 819.778320312 628.079101562 826.493164062 634.793945312 834.77734375 634.794921875 c 2 - 964.908203125 634.794921875 l 2 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni23EE -Encoding: 9198 9198 8 -Width: 1000 -VWidth: 0 -VStem: 20.1074 160.131<-34.8008 280.725 319.277 634.795> -LayerCount: 2 -Fore -SplineSet -35.107421875 634.794921875 m 2 - 165.23828125 634.794921875 l 2 - 173.522460938 634.793945312 180.237304688 628.079101562 180.23828125 619.794921875 c 2 - 180.23828125 319.27734375 l 1 - 555.635742188 631.466796875 l 2 - 558.186523438 633.526367188 561.342773438 634.693359375 564.620117188 634.7890625 c 0 - 573.073242188 635.038085938 580.0625 628.251953125 580.063476562 619.794921875 c 2 - 580.063476562 319.26171875 l 1 - 955.482421875 631.466796875 l 2 - 958.033203125 633.526367188 961.189453125 634.693359375 964.466796875 634.7890625 c 0 - 972.919921875 635.038085938 979.907226562 628.251953125 979.908203125 619.794921875 c 2 - 979.908203125 -19.80078125 l 2 - 979.901367188 -32.408203125 965.291015625 -39.3857421875 955.482421875 -31.46484375 c 2 - 580.063476562 280.740234375 l 1 - 580.063476562 -19.80078125 l 2 - 580.057617188 -32.408203125 565.444335938 -39.3857421875 555.635742188 -31.46484375 c 2 - 180.23828125 280.724609375 l 1 - 180.23828125 -19.80078125 l 2 - 180.237304688 -28.0849609375 173.522460938 -34.7998046875 165.23828125 -34.80078125 c 2 - 35.107421875 -34.80078125 l 2 - 26.8232421875 -34.7998046875 20.1083984375 -28.0849609375 20.107421875 -19.80078125 c 2 - 20.107421875 619.794921875 l 2 - 20.1083984375 628.079101562 26.8232421875 634.793945312 35.107421875 634.794921875 c 2 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni23EF -Encoding: 9199 9199 9 -Width: 1000 -VWidth: 0 -VStem: 547.777 160.131<-34.8008 634.795> 819.777 160.131<-34.8008 634.795> -LayerCount: 2 -Fore -SplineSet -34.798828125 634.794921875 m 0 - 38.3349609375 634.868164062 41.783203125 633.689453125 44.53515625 631.466796875 c 2 - 429.349609375 311.66796875 l 2 - 436.778320312 305.6640625 436.778320312 294.337890625 429.349609375 288.333984375 c 2 - 44.53515625 -31.46484375 l 2 - 34.7265625 -39.3857421875 20.1142578125 -32.408203125 20.107421875 -19.80078125 c 2 - 20.107421875 619.794921875 l 2 - 20.1064453125 627.958984375 26.6357421875 634.625 34.798828125 634.794921875 c 0 -562.77734375 634.794921875 m 2 - 692.908203125 634.794921875 l 2 - 701.192382812 634.793945312 707.907226562 628.079101562 707.908203125 619.794921875 c 2 - 707.908203125 -19.80078125 l 2 - 707.907226562 -28.0849609375 701.192382812 -34.7998046875 692.908203125 -34.80078125 c 2 - 562.77734375 -34.80078125 l 2 - 554.493164062 -34.7998046875 547.778320312 -28.0849609375 547.77734375 -19.80078125 c 2 - 547.77734375 619.794921875 l 2 - 547.778320312 628.079101562 554.493164062 634.793945312 562.77734375 634.794921875 c 2 -834.77734375 634.794921875 m 2 - 964.908203125 634.794921875 l 2 - 973.192382812 634.793945312 979.907226562 628.079101562 979.908203125 619.794921875 c 2 - 979.908203125 -19.80078125 l 2 - 979.907226562 -28.0849609375 973.192382812 -34.7998046875 964.908203125 -34.80078125 c 2 - 834.77734375 -34.80078125 l 2 - 826.493164062 -34.7998046875 819.778320312 -28.0849609375 819.77734375 -19.80078125 c 2 - 819.77734375 619.794921875 l 2 - 819.778320312 628.079101562 826.493164062 634.793945312 834.77734375 634.794921875 c 2 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni2630 -Encoding: 9776 9776 10 -Width: 1000 -VWidth: 0 -HStem: -33.625 80.1309<165.202 834.798> 259.935 80.1309<165.202 834.798> 553.494 80.1309<165.202 834.798> -CounterMasks: 1 e0 -LayerCount: 2 -Fore -SplineSet -834.797851562 325.065429688 m 2 - 834.797851562 274.934570312 l 2 - 834.796875 266.650390625 828.08203125 259.935546875 819.797851562 259.934570312 c 2 - 180.202148438 259.934570312 l 2 - 171.91796875 259.935546875 165.203125 266.650390625 165.202148438 274.934570312 c 2 - 165.202148438 325.065429688 l 2 - 165.203125 333.349609375 171.91796875 340.064453125 180.202148438 340.065429688 c 2 - 819.797851562 340.065429688 l 2 - 828.08203125 340.064453125 834.796875 333.349609375 834.797851562 325.065429688 c 2 -834.797851562 31.505859375 m 2 - 834.797851562 -18.625 l 2 - 834.796875 -26.9091796875 828.08203125 -33.6240234375 819.797851562 -33.625 c 2 - 180.202148438 -33.625 l 2 - 171.91796875 -33.6240234375 165.203125 -26.9091796875 165.202148438 -18.625 c 2 - 165.202148438 31.505859375 l 2 - 165.203125 39.7900390625 171.91796875 46.5048828125 180.202148438 46.505859375 c 2 - 819.797851562 46.505859375 l 2 - 828.08203125 46.5048828125 834.796875 39.7900390625 834.797851562 31.505859375 c 2 -834.797851562 618.625 m 2 - 834.797851562 568.494140625 l 2 - 834.796875 560.209960938 828.08203125 553.495117188 819.797851562 553.494140625 c 2 - 180.202148438 553.494140625 l 2 - 171.91796875 553.495117188 165.203125 560.209960938 165.202148438 568.494140625 c 2 - 165.202148438 618.625 l 2 - 165.203125 626.909179688 171.91796875 633.624023438 180.202148438 633.625 c 2 - 819.797851562 633.625 l 2 - 828.08203125 633.624023438 834.796875 626.909179688 834.797851562 618.625 c 2 -EndSplineSet -Validated: 1 -EndChar - -StartChar: uni2B60 -Encoding: 11104 11104 11 -Width: 1000 -HStem: 219.932 160.131<434.739 979.995> -LayerCount: 2 -Fore -SplineSet -420.047851562 634.794921875 m 0 - 428.2109375 634.625 434.740234375 627.958984375 434.739257812 619.794921875 c 2 - 434.739257812 395.0625 l 2 - 434.740234375 386.778320312 441.455078125 380.063476562 449.739257812 380.0625 c 2 - 964.995117188 380.0625 l 2 - 973.279296875 380.061523438 979.994140625 373.346679688 979.995117188 365.0625 c 2 - 979.995117188 234.931640625 l 2 - 979.994140625 226.647460938 973.279296875 219.932617188 964.995117188 219.931640625 c 2 - 449.739257812 219.931640625 l 2 - 441.455078125 219.930664062 434.740234375 213.215820312 434.739257812 204.931640625 c 2 - 434.739257812 -19.80078125 l 2 - 434.731445312 -32.408203125 420.120117188 -39.3857421875 410.311523438 -31.46484375 c 2 - 25.4970703125 288.333984375 l 2 - 18.068359375 294.337890625 18.068359375 305.6640625 25.4970703125 311.66796875 c 2 - 410.311523438 631.466796875 l 2 - 413.063476562 633.689453125 416.51171875 634.868164062 420.047851562 634.794921875 c 0 -EndSplineSet -EndChar - -StartChar: uni2B62 -Encoding: 11106 11106 12 -Width: 1000 -HStem: 219.932 160.131<19.9258 565.182> -LayerCount: 2 -Fore -SplineSet -579.873046875 634.794921875 m 0 - 583.409179688 634.868164062 586.857421875 633.689453125 589.609375 631.466796875 c 2 - 974.423828125 311.66796875 l 2 - 981.852539062 305.6640625 981.852539062 294.337890625 974.423828125 288.333984375 c 2 - 589.609375 -31.46484375 l 2 - 579.80078125 -39.3857421875 565.189453125 -32.408203125 565.181640625 -19.80078125 c 2 - 565.181640625 204.931640625 l 2 - 565.180664062 213.215820312 558.465820312 219.930664062 550.181640625 219.931640625 c 2 - 34.92578125 219.931640625 l 2 - 26.6416015625 219.932617188 19.9267578125 226.647460938 19.92578125 234.931640625 c 2 - 19.92578125 365.0625 l 2 - 19.9267578125 373.346679688 26.6416015625 380.061523438 34.92578125 380.0625 c 2 - 550.181640625 380.0625 l 2 - 558.465820312 380.063476562 565.180664062 386.778320312 565.181640625 395.0625 c 2 - 565.181640625 619.794921875 l 2 - 565.180664062 627.958984375 571.7109375 634.625 579.873046875 634.794921875 c 0 -EndSplineSet -EndChar - -StartChar: u1FA9F -Encoding: 129695 129695 13 -Width: 1000 -HStem: -180.002 439.97<20.001 459.968 540.034 980.001> 340.034 439.97<20.001 459.968 540.034 980.001> -VStem: 20.001 439.967<-180.002 259.968 340.034 780.004> 540.034 439.967<-180.002 259.968 340.034 780.004> -LayerCount: 2 -Fore -SplineSet -546.908203125 259.967773438 m 2 - 973.126953125 259.967773438 l 2 - 976.934570312 259.967773438 980.000976562 256.90234375 980.000976562 253.09375 c 2 - 980.000976562 -173.127929688 l 2 - 980.000976562 -176.935546875 976.934570312 -180.001953125 973.126953125 -180.001953125 c 2 - 546.908203125 -180.001953125 l 2 - 543.099609375 -180.001953125 540.034179688 -176.935546875 540.034179688 -173.127929688 c 2 - 540.034179688 253.09375 l 2 - 540.034179688 256.90234375 543.099609375 259.967773438 546.908203125 259.967773438 c 2 -26.8759765625 259.967773438 m 2 - 453.09375 259.967773438 l 2 - 456.901367188 259.967773438 459.967773438 256.90234375 459.967773438 253.09375 c 2 - 459.967773438 -173.127929688 l 2 - 459.967773438 -176.935546875 456.901367188 -180.001953125 453.09375 -180.001953125 c 2 - 26.8759765625 -180.001953125 l 2 - 23.0673828125 -180.001953125 20.0009765625 -176.935546875 20.0009765625 -173.127929688 c 2 - 20.0009765625 253.09375 l 2 - 20.0009765625 256.90234375 23.0673828125 259.967773438 26.8759765625 259.967773438 c 2 -546.908203125 780.00390625 m 2 - 973.126953125 780.00390625 l 2 - 976.934570312 780.00390625 980.000976562 776.9375 980.000976562 773.12890625 c 2 - 980.000976562 346.909179688 l 2 - 980.000976562 343.100585938 976.934570312 340.034179688 973.126953125 340.034179688 c 2 - 546.908203125 340.034179688 l 2 - 543.099609375 340.034179688 540.034179688 343.100585938 540.034179688 346.909179688 c 2 - 540.034179688 773.12890625 l 2 - 540.034179688 776.9375 543.099609375 780.00390625 546.908203125 780.00390625 c 2 -26.8759765625 780.00390625 m 2 - 453.09375 780.00390625 l 2 - 456.901367188 780.00390625 459.967773438 776.9375 459.967773438 773.12890625 c 2 - 459.967773438 346.909179688 l 2 - 459.967773438 343.100585938 456.901367188 340.034179688 453.09375 340.034179688 c 2 - 26.8759765625 340.034179688 l 2 - 23.0673828125 340.034179688 20.0009765625 343.100585938 20.0009765625 346.909179688 c 2 - 20.0009765625 773.12890625 l 2 - 20.0009765625 776.9375 23.0673828125 780.00390625 26.8759765625 780.00390625 c 2 -EndSplineSet -EndChar -EndChars -EndSplineFont diff --git a/backend.go b/inputcontrol/controller.go similarity index 62% rename from backend.go rename to inputcontrol/controller.go index dfddde3..111e937 100644 --- a/backend.go +++ b/inputcontrol/controller.go @@ -17,10 +17,14 @@ * along with Remote-Touchpad. If not, see . */ -package main +package inputcontrol -type PointerButton int -type Key int +import "sort" + +type ( + PointerButton int + Key int +) const ( PointerButtonLeft PointerButton = iota @@ -39,34 +43,51 @@ const ( KeyBrowserBack KeyBrowserForward KeySuper + KeyLeft + KeyRight + KeyUp + KeyDown + KeyHome + KeyEnd + KeyBackSpace + KeyDelete + KeyReturn KeyLimit ) -type BackendInfo struct { +type ControllerInfo struct { Name string - Init func() (Backend, error) + Init func() (Controller, error) + + priority int } -var Backends []BackendInfo = []BackendInfo{ - {"X11", InitX11Backend}, - {"RemoteDesktop portal", InitPortalBackend}, - {"Windows", InitWindowsBackend}, +var Controllers []ControllerInfo + +func RegisterController(name string, init func() (Controller, error), priority int) { + Controllers = append(Controllers, ControllerInfo{name, init, priority}) + sort.SliceStable(Controllers, func(i, j int) bool { + return Controllers[i].priority < Controllers[j].priority + }) } type UnsupportedPlatformError struct { - err error + Err error } -func (e UnsupportedPlatformError) Error() string { - return e.err.Error() +func (e *UnsupportedPlatformError) Error() string { + return e.Err.Error() } -type Backend interface { +func (e *UnsupportedPlatformError) Unwrap() error { + return e.Err +} + +type Controller interface { Close() error KeyboardText(text string) error KeyboardKey(key Key) error PointerButton(button PointerButton, press bool) error PointerMove(deltaX, deltaY int) error - PointerScroll(deltaHorizontal, deltaVertical int) error - PointerScrollFinish() error + PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error } diff --git a/inputcontrol/controller_null.go b/inputcontrol/controller_null.go new file mode 100644 index 0000000..6188cce --- /dev/null +++ b/inputcontrol/controller_null.go @@ -0,0 +1,66 @@ +//go:build null + +/* + * Copyright (c) 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +package inputcontrol + +import ( + "log" +) + +type nullController struct{} + +func init() { + RegisterController("null", InitNullController, 1000) +} + +func InitNullController() (Controller, error) { + return &nullController{}, nil +} + +func (p *nullController) Close() error { + return nil +} + +func (p *nullController) KeyboardText(text string) error { + log.Printf("KeyboardText(text: %#v)", text) + return nil +} + +func (p *nullController) KeyboardKey(key Key) error { + log.Printf("KeyboardKey(key: %#v)", key) + return nil +} + +func (p *nullController) PointerButton(button PointerButton, press bool) error { + log.Printf("PointerButton(button: %#v, press: %#v)", button, press) + return nil +} + +func (p *nullController) PointerMove(deltaX, deltaY int) error { + log.Printf("PointerMove(deltaX: %#v, deltaY: %#v)", deltaX, deltaY) + return nil +} + +func (p *nullController) PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error { + log.Printf("PointerScroll(deltaHorizontal: %#v, deltaVertical: %#v, finish: %#v)", + deltaHorizontal, deltaVertical, finish) + return nil +} diff --git a/inputcontrol/controller_portal.go b/inputcontrol/controller_portal.go new file mode 100644 index 0000000..99ecf8b --- /dev/null +++ b/inputcontrol/controller_portal.go @@ -0,0 +1,400 @@ +//go:build portal + +/* + * Copyright (c) 2018 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +package inputcontrol + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hkdf" + "crypto/rand" + "crypto/sha256" + "errors" + "fmt" + "io" + "log" + "os" + "path/filepath" + "slices" + + "github.com/godbus/dbus/v5" +) + +const ( + deviceKeyboard uint32 = 1 + devicePointer uint32 = 2 + + btnReleased uint32 = 0 + btnPressed uint32 = 1 + + untilRevoked uint32 = 2 + + // linux/input-event-codes.h + btnLeft int32 = 0x110 + btnRight int32 = 0x111 + btnMiddle int32 = 0x112 +) + +type portalController struct { + bus *dbus.Conn + portalDesktop dbus.BusObject + sessionHandle dbus.ObjectPath +} + +func init() { + RegisterController("RemoteDesktop portal", InitPortalController, 1) +} + +func InitPortalController() (Controller, error) { + bus, err := dbus.SessionBusPrivate() + if err != nil { + return nil, &UnsupportedPlatformError{err} + } + cleanupBus := true + defer func() { + if cleanupBus { + bus.Close() + } + }() + err = bus.Auth(nil) + if err != nil { + return nil, &UnsupportedPlatformError{err} + } + err = bus.Hello() + if err != nil { + return nil, &UnsupportedPlatformError{err} + } + portalDesktop := bus.Object("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop") + remoteDesktopVersionV, err := portalDesktop.GetProperty( + "org.freedesktop.portal.RemoteDesktop.version") + if err != nil { + return nil, &UnsupportedPlatformError{ + fmt.Errorf("getting 'version' failed: %w", err), + } + } + remoteDesktopVersion, ok := remoteDesktopVersionV.Value().(uint32) + if !ok { + return nil, &UnsupportedPlatformError{ + errors.New("unexpected 'version' type"), + } + } + restoreTokenStore, err := func() (*secretStore, error) { + if remoteDesktopVersion < 2 { + return nil, nil + } + cacheDirectory, err := os.UserCacheDir() + if err != nil { + return nil, err + } + if err := os.MkdirAll(cacheDirectory, 0o700); err != nil { + return nil, err + } + secret, err := retrieveSecret(bus) + if err != nil { + return nil, err + } + return newSecretStore(secret, + filepath.Join(cacheDirectory, "remote-touchpad.portal-restore-token.bin")) + }() + if err != nil { + log.Printf("Skipping restore token: %v", err) + } + availableDeviceTypesV, err := portalDesktop.GetProperty( + "org.freedesktop.portal.RemoteDesktop.AvailableDeviceTypes") + if err != nil { + return nil, &UnsupportedPlatformError{ + fmt.Errorf("getting 'AvailableDeviceTypes' failed: %w", err), + } + } + availableDeviceTypes, ok := availableDeviceTypesV.Value().(uint32) + if !ok { + return nil, &UnsupportedPlatformError{ + errors.New("unexpected 'AvailableDeviceTypes' return type"), + } + } + if availableDeviceTypes&deviceKeyboard == 0 || + availableDeviceTypes&devicePointer == 0 { + return nil, &UnsupportedPlatformError{ + errors.New("keyboard or pointer source type not supported"), + } + } + createSessionResults, err := checkResponse(getResponse(bus, portalDesktop, + "org.freedesktop.portal.RemoteDesktop.CreateSession", 0, + map[string]dbus.Variant{"session_handle_token": dbus.MakeVariant("t")}, + )) + if err != nil { + return nil, &UnsupportedPlatformError{ + fmt.Errorf("calling 'CreateSession' failed: %w", err), + } + } + sessionHandleString, ok := createSessionResults["session_handle"].Value().(string) + if !ok { + return nil, &UnsupportedPlatformError{ + errors.New("unexpected 'session_handle' type in 'CreateSession' return value"), + } + } + sessionHandle := dbus.ObjectPath(sessionHandleString) + selectDevicesOptions := map[string]dbus.Variant{ + "types": dbus.MakeVariant(deviceKeyboard | devicePointer), + } + if restoreTokenStore != nil { + if restoreToken, err := restoreTokenStore.Load(); err != nil { + if !errors.Is(err, os.ErrNotExist) { + log.Printf("Failed to load restore token: %v", err) + } + } else if len(restoreToken) > 0 { + selectDevicesOptions["restore_token"] = dbus.MakeVariant(string(restoreToken)) + } + selectDevicesOptions["persist_mode"] = dbus.MakeVariant(untilRevoked) + } + _, err = checkResponse(getResponse(bus, portalDesktop, + "org.freedesktop.portal.RemoteDesktop.SelectDevices", 0, + sessionHandle, selectDevicesOptions, + )) + if err != nil { + return nil, &UnsupportedPlatformError{ + fmt.Errorf("calling 'SelectDevices' failed: %w", err), + } + } + startResponseStatus, startResults, err := getResponse(bus, portalDesktop, + "org.freedesktop.portal.RemoteDesktop.Start", 0, + sessionHandle, "", map[string]dbus.Variant{}, + ) + if err != nil { + return nil, &UnsupportedPlatformError{ + fmt.Errorf("calling 'Start' failed: %w", err), + } + } + if startResponseStatus != 0 { + return nil, errors.New("keyboard or pointer access denied") + } + if restoreToken, _ := startResults["restore_token"].Value().(string); restoreTokenStore != nil { + if err := restoreTokenStore.Store([]byte(restoreToken)); err != nil { + log.Printf("Failed to store restore token: %v", err) + } + } + devices, ok := startResults["devices"].Value().(uint32) + if !ok { + return nil, &UnsupportedPlatformError{ + errors.New("unexpected 'devices' type in 'Start' return value"), + } + } + if devices&deviceKeyboard == 0 || devices&devicePointer == 0 { + return nil, errors.New("keyboard or pointer access denied") + } + cleanupBus = false + return &portalController{ + bus: bus, portalDesktop: portalDesktop, + sessionHandle: sessionHandle, + }, nil +} + +func retrieveSecret(bus *dbus.Conn) ([]byte, error) { + portalDesktop := bus.Object("org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop") + secretReader, secretWriter, err := os.Pipe() + if err != nil { + return nil, err + } + defer secretReader.Close() + defer secretWriter.Close() + if _, err := checkResponse(getResponse(bus, portalDesktop, + "org.freedesktop.portal.Secret.RetrieveSecret", 0, + dbus.UnixFD(secretWriter.Fd()), map[string]dbus.Variant{}, + )); err != nil { + return nil, fmt.Errorf("calling 'RetrieveSecret' failed: %w", err) + } + if err := secretWriter.Close(); err != nil { + return nil, err + } + secret, err := io.ReadAll(secretReader) + if err != nil { + return nil, err + } + if len(secret) < 16 { + return nil, fmt.Errorf("'RetrieveSecret' returned too few bytes (%d)", len(secret)) + } + return secret, err +} + +type secretStore struct { + aesgcm cipher.AEAD + filename string +} + +func newSecretStore(key []byte, filename string) (*secretStore, error) { + derivedKey, err := hkdf.Key(sha256.New, key, nil, "", 32) + if err != nil { + return nil, err + } + block, err := aes.NewCipher(derivedKey) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + return &secretStore{ + aesgcm: aesgcm, + filename: filename, + }, nil +} + +func (s *secretStore) Load() ([]byte, error) { + data, err := os.ReadFile(s.filename) + if err != nil { + return nil, err + } + if len(data) < s.aesgcm.NonceSize() { + return nil, errors.New("invalid ciphertext") + } + nonce := data[:s.aesgcm.NonceSize()] + ciphertext := data[len(nonce):] + return s.aesgcm.Open(nil, nonce, ciphertext, nil) +} + +func (s *secretStore) Store(data []byte) error { + nonce := make([]byte, s.aesgcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return err + } + ciphertext := s.aesgcm.Seal(nil, nonce, data, nil) + return os.WriteFile(s.filename, slices.Concat(nonce, ciphertext), 0o600) +} + +func getResponse(bus *dbus.Conn, object dbus.BusObject, method string, + flags dbus.Flags, args ...interface{}, +) (uint32, map[string]dbus.Variant, error) { + ch := make(chan *dbus.Signal, 512) + bus.Signal(ch) + defer bus.RemoveSignal(ch) + var requestPath dbus.ObjectPath + if err := object.Call(method, flags, args...).Store(&requestPath); err != nil { + return 0, nil, err + } + for { + s := <-ch + if s.Path == requestPath && s.Name == "org.freedesktop.portal.Request.Response" { + if len(s.Body) != 2 { + return 0, nil, fmt.Errorf("unexpected 'Response' return length (%d)", len(s.Body)) + } + responseStatus, ok := s.Body[0].(uint32) + if !ok { + return 0, nil, errors.New("unexpected 'Response' return type") + } + results, ok := s.Body[1].(map[string]dbus.Variant) + if !ok { + return 0, nil, errors.New("unexpected 'Response' return type") + } + return responseStatus, results, nil + } + } +} + +func checkResponse(responseStatus uint32, results map[string]dbus.Variant, err error) (map[string]dbus.Variant, error) { + if err == nil && responseStatus != 0 { + err = fmt.Errorf("unexpected 'Response' status (%d)", responseStatus) + } + return results, err +} + +func (p *portalController) Close() error { + return p.bus.Close() +} + +func (p *portalController) keyboardKeys(keys []Keysym) error { + for _, keysym := range keys { + for _, state := range [...]uint32{btnPressed, btnReleased} { + if err := p.portalDesktop.Call( + "org.freedesktop.portal.RemoteDesktop.NotifyKeyboardKeysym", 0, + p.sessionHandle, map[string]dbus.Variant{}, keysym, state, + ).Store(); err != nil { + return err + } + } + } + return nil +} + +func (p *portalController) KeyboardText(text string) error { + keys := make([]Keysym, 0, len(text)) + for _, runeValue := range text { + keysym, err := RuneToKeysym(runeValue) + if err != nil { + return err + } + keys = append(keys, keysym) + } + return p.keyboardKeys(keys) +} + +func (p *portalController) KeyboardKey(key Key) error { + keysym, err := KeyToKeysym(key) + if err != nil { + return err + } + keys := [...]Keysym{keysym} + return p.keyboardKeys(keys[:]) +} + +func (p *portalController) PointerButton(button PointerButton, press bool) error { + var btn int32 + switch button { + case PointerButtonLeft: + btn = btnLeft + case PointerButtonMiddle: + btn = btnMiddle + case PointerButtonRight: + btn = btnRight + default: + return fmt.Errorf("unsupported pointer button: %#v", button) + } + state := btnReleased + if press { + state = btnPressed + } + if err := p.portalDesktop.Call("org.freedesktop.portal.RemoteDesktop.NotifyPointerButton", 0, + p.sessionHandle, map[string]dbus.Variant{}, btn, state, + ).Store(); err != nil { + return err + } + return nil +} + +func (p *portalController) PointerMove(deltaX, deltaY int) error { + if err := p.portalDesktop.Call("org.freedesktop.portal.RemoteDesktop.NotifyPointerMotion", 0, + p.sessionHandle, map[string]dbus.Variant{}, float64(deltaX), float64(deltaY), + ).Store(); err != nil { + return err + } + return nil +} + +func (p *portalController) PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error { + if err := p.portalDesktop.Call("org.freedesktop.portal.RemoteDesktop.NotifyPointerAxis", 0, + p.sessionHandle, map[string]dbus.Variant{"finish": dbus.MakeVariant(finish)}, float64(deltaHorizontal), float64(deltaVertical), + ).Store(); err != nil { + return err + } + return nil +} diff --git a/inputcontrol/controller_uinput.go b/inputcontrol/controller_uinput.go new file mode 100644 index 0000000..2dd9bab --- /dev/null +++ b/inputcontrol/controller_uinput.go @@ -0,0 +1,200 @@ +//go:build uinput + +/* + * Copyright (c) 2023 De_Coder github.com/ps100000 + * Copyright (c) 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +package inputcontrol + +import ( + "errors" + "fmt" + "log" + "os" + "time" + + "github.com/bendahl/uinput" +) + +const shiftKeysDelay time.Duration = 50 * time.Millisecond + +type uinputController struct { + keymap *Keymap + keyboard uinput.Keyboard + mouse uinput.Mouse +} + +func init() { + RegisterController("uinput", InitUinputController, 10) +} + +func InitUinputController() (Controller, error) { + keymapName, keymapSet := os.LookupEnv("REMOTE_TOUCHPAD_UINPUT_KEYMAP") + if !keymapSet { + keymapName = "defkeymap" + } + keymap, err := LoadKeymap(keymapName) + if err != nil { + return nil, err + } + keyboard, err := uinput.CreateKeyboard("/dev/uinput", []byte("remote-touchpad-keyboard")) + if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) { + err = &UnsupportedPlatformError{err} + } + if err != nil { + return nil, err + } + mouse, err := uinput.CreateMouse("/dev/uinput", []byte("remote-touchpad-mouse")) + if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) { + err = &UnsupportedPlatformError{err} + } + if err != nil { + keyboard.Close() + return nil, err + } + if !keymapSet { + log.Print("Hint: Set the keyboard mapping with the REMOTE_TOUCHPAD_UINPUT_KEYMAP environment variable") + } + return &uinputController{keymap, keyboard, mouse}, nil +} + +func (p *uinputController) Close() error { + return errors.Join(p.keyboard.Close(), p.mouse.Close()) +} + +func (p *uinputController) KeyboardText(text string) error { + var activeShiftKeys KeyCombo + updateShiftKeys := func(keyCombo KeyCombo) error { + if activeShiftKeys.ShiftKeys == keyCombo.ShiftKeys { + activeShiftKeys.Key = keyCombo.Key + return nil + } + if activeShiftKeys.Key != 0 { + time.Sleep(shiftKeysDelay) + activeShiftKeys.Key = 0 + } + for i := range keyCombo.ShiftKeys { + if activeShiftKeys.ShiftKeys[i] != keyCombo.ShiftKeys[i] { + if activeShiftKeys.ShiftKeys[i] != 0 { + if err := p.keyboard.KeyUp(activeShiftKeys.ShiftKeys[i]); err != nil { + return err + } + activeShiftKeys.ShiftKeys[i] = 0 + } + if keyCombo.ShiftKeys[i] != 0 { + if err := p.keyboard.KeyDown(keyCombo.ShiftKeys[i]); err != nil { + return err + } + activeShiftKeys.ShiftKeys[i] = keyCombo.ShiftKeys[i] + } + } + } + if keyCombo.Key != 0 { + activeShiftKeys.Key = keyCombo.Key + time.Sleep(shiftKeysDelay) + } + return nil + } + defer updateShiftKeys(KeyCombo{}) + for _, runeValue := range text { + keyCombo, found := p.keymap.Get(runeValue) + if !found { + return fmt.Errorf("unsupported rune: %q", runeValue) + } + if err := updateShiftKeys(keyCombo); err != nil { + return err + } + if err := p.keyboard.KeyPress(keyCombo.Key); err != nil { + return err + } + } + return updateShiftKeys(KeyCombo{Key: 1}) +} + +func (p *uinputController) KeyboardKey(key Key) error { + var uinputKey int + switch key { + case KeyBackSpace: + uinputKey = uinput.KeyBackspace + case KeyReturn: + uinputKey = uinput.KeyEnter + case KeyDelete: + uinputKey = uinput.KeyDelete + case KeyHome: + uinputKey = uinput.KeyHome + case KeyLeft: + uinputKey = uinput.KeyLeft + case KeyUp: + uinputKey = uinput.KeyUp + case KeyRight: + uinputKey = uinput.KeyRight + case KeyDown: + uinputKey = uinput.KeyDown + case KeyEnd: + uinputKey = uinput.KeyEnd + case KeySuper: + uinputKey = uinput.KeyLeftmeta + case KeyVolumeMute: + uinputKey = uinput.KeyMute + case KeyVolumeDown: + uinputKey = uinput.KeyVolumedown + case KeyVolumeUp: + uinputKey = uinput.KeyVolumeup + case KeyMediaPlayPause: + uinputKey = uinput.KeyPlaypause + case KeyMediaPrevTrack: + uinputKey = uinput.KeyPrevioussong + case KeyMediaNextTrack: + uinputKey = uinput.KeyNextsong + case KeyBrowserBack: + uinputKey = uinput.KeyBack + case KeyBrowserForward: + uinputKey = uinput.KeyForward + default: + return fmt.Errorf("unsupported key: %#v", key) + } + return p.keyboard.KeyPress(uinputKey) +} + +func (p *uinputController) PointerButton(button PointerButton, press bool) error { + switch { + case button == PointerButtonLeft && press: + return p.mouse.LeftPress() + case button == PointerButtonLeft && !press: + return p.mouse.LeftRelease() + case button == PointerButtonRight && press: + return p.mouse.RightPress() + case button == PointerButtonRight && !press: + return p.mouse.RightRelease() + case button == PointerButtonMiddle && press: + return p.mouse.MiddlePress() + case button == PointerButtonMiddle && !press: + return p.mouse.MiddleRelease() + default: + return fmt.Errorf("unsupported pointer button: %#v", button) + } +} + +func (p *uinputController) PointerMove(deltaX, deltaY int) error { + return p.mouse.Move(int32(deltaX), int32(deltaY)) +} + +func (p *uinputController) PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error { + return errors.Join(p.mouse.Wheel(false, int32(-deltaVertical)), p.mouse.Wheel(true, int32(deltaHorizontal))) +} diff --git a/backend_windows.go b/inputcontrol/controller_windows.go similarity index 63% rename from backend_windows.go rename to inputcontrol/controller_windows.go index 4ff3d04..58b5e9a 100644 --- a/backend_windows.go +++ b/inputcontrol/controller_windows.go @@ -1,4 +1,4 @@ -// +build windows +//go:build windows /* * Copyright (c) 2018 Unrud @@ -19,10 +19,10 @@ * along with Remote-Touchpad. If not, see . */ -package main +package inputcontrol import ( - "errors" + "fmt" "syscall" "unsafe" ) @@ -34,6 +34,15 @@ const ( keyeventfKeyup uint32 = 0x2 keyeventfUnicode uint32 = 0x4 + vkBack uint16 = 0x8 + vkReturn uint16 = 0xD + vkEnd uint16 = 0x23 + vkHome uint16 = 0x24 + vkLeft uint16 = 0x25 + vkUp uint16 = 0x26 + vkRight uint16 = 0x27 + vkDown uint16 = 0x28 + vkDelete uint16 = 0x2E vkLwin uint16 = 0x5B vkBrowserBack uint16 = 0xA6 vkBrowserForward uint16 = 0xA7 @@ -80,33 +89,36 @@ type keybdInput struct { padding [8]byte } -type windowsBackend struct{} +type windowsController struct{} -func InitWindowsBackend() (Backend, error) { - p := &windowsBackend{} +func init() { + RegisterController("Windows", InitWindowsController, 0) +} + +func InitWindowsController() (Controller, error) { + p := &windowsController{} if err := sendInputProc.Find(); err != nil { - return nil, UnsupportedPlatformError{err} + return nil, &UnsupportedPlatformError{err} } return p, nil } -func (p *windowsBackend) Close() error { +func (p *windowsController) Close() error { return nil } -func (p *windowsBackend) sendInput(inputs []keybdInput) error { +func (p *windowsController) sendInput(inputs []keybdInput) error { if len(inputs) == 0 { return nil } - if r, _, err := sendInputProc.Call(uintptr(len(inputs)), - uintptr(unsafe.Pointer(&inputs[0])), - unsafe.Sizeof(inputs[0])); int(r) != len(inputs) { + if sent, _, err := sendInputProc.Call(uintptr(len(inputs)), + uintptr(unsafe.Pointer(&inputs[0])), unsafe.Sizeof(inputs[0])); int(sent) != len(inputs) { return err } return nil } -func (p *windowsBackend) KeyboardText(text string) error { +func (p *windowsController) KeyboardText(text string) error { inputs := make([]keybdInput, 0, len(text)*2) for _, runeValue := range text { in := keybdInput{typ: inputKeyboard, wScan: uint16(runeValue), dwFlags: keyeventfUnicode} @@ -120,35 +132,54 @@ func (p *windowsBackend) KeyboardText(text string) error { return p.sendInput(inputs) } -func (p *windowsBackend) KeyboardKey(key Key) error { +func (p *windowsController) KeyboardKey(key Key) error { input := keybdInput{typ: inputKeyboard} - if key == KeySuper { + switch key { + case KeyBackSpace: + input.wVk = vkBack + case KeyReturn: + input.wVk = vkReturn + case KeyEnd: + input.wVk = vkEnd + case KeyHome: + input.wVk = vkHome + case KeyLeft: + input.wVk = vkLeft + case KeyUp: + input.wVk = vkUp + case KeyRight: + input.wVk = vkRight + case KeyDown: + input.wVk = vkDown + case KeyDelete: + input.wVk = vkDelete + case KeySuper: input.wVk = vkLwin - } else if key == KeyBrowserBack { + case KeyBrowserBack: input.wVk = vkBrowserBack - } else if key == KeyBrowserForward { + case KeyBrowserForward: input.wVk = vkBrowserForward - } else if key == KeyVolumeMute { + case KeyVolumeMute: input.wVk = vkVolumeMute - } else if key == KeyVolumeDown { + case KeyVolumeDown: input.wVk = vkVolumeDown - } else if key == KeyVolumeUp { + case KeyVolumeUp: input.wVk = vkVolumeUp - } else if key == KeyMediaNextTrack { + case KeyMediaNextTrack: input.wVk = vkMediaNextTrack - } else if key == KeyMediaPrevTrack { + case KeyMediaPrevTrack: input.wVk = vkMediaPrevTrack - } else if key == KeyMediaPlayPause { + case KeyMediaPlayPause: input.wVk = vkMediaPlayPause - } else { - return errors.New("key not mapped to virtual-key code") + default: + return fmt.Errorf("key not mapped to virtual-key code: %#v", key) } inputs := [...]keybdInput{input, input} inputs[1].dwFlags |= keyeventfKeyup return p.sendInput(inputs[:]) } -func (p *windowsBackend) PointerButton(button PointerButton, press bool) error { +func (p *windowsController) PointerButton(button PointerButton, press bool) error { input := mouseInput{typ: inputMouse} if button == PointerButtonLeft && press { input.dwFlags = mouseeventfLeftdown @@ -163,30 +194,30 @@ func (p *windowsBackend) PointerButton(button PointerButton, press bool) error { } else if button == PointerButtonRight { input.dwFlags = mouseeventfRightup } else { - return errors.New("unsupported pointer button") + return fmt.Errorf("unsupported pointer button: %#v", button) } - if r, _, err := sendInputProc.Call(1, uintptr(unsafe.Pointer(&input)), - unsafe.Sizeof(input)); int(r) != 1 { + if sent, _, err := sendInputProc.Call(1, uintptr(unsafe.Pointer(&input)), + unsafe.Sizeof(input)); int(sent) != 1 { return err } return nil } -func (p *windowsBackend) PointerMove(deltaX, deltaY int) error { +func (p *windowsController) PointerMove(deltaX, deltaY int) error { input := mouseInput{ typ: inputMouse, dx: int32(deltaX), dy: int32(deltaY), dwFlags: mouseeventfMove, } - if r, _, err := sendInputProc.Call(1, uintptr(unsafe.Pointer(&input)), - unsafe.Sizeof(input)); int(r) != 1 { + if sent, _, err := sendInputProc.Call(1, uintptr(unsafe.Pointer(&input)), + unsafe.Sizeof(input)); int(sent) != 1 { return err } return nil } -func (p *windowsBackend) PointerScroll(deltaHorizontal, deltaVertical int) error { +func (p *windowsController) PointerScroll(deltaHorizontal, deltaVertical int, finish bool) error { inputs := make([]mouseInput, 0, 2) if deltaHorizontal != 0 { inputs = append(inputs, mouseInput{ @@ -199,20 +230,15 @@ func (p *windowsBackend) PointerScroll(deltaHorizontal, deltaVertical int) error inputs = append(inputs, mouseInput{ typ: inputMouse, dwFlags: mouseeventfWheel, - mouseData: uint32(deltaVertical * scrollMult), + mouseData: uint32(-deltaVertical * scrollMult), }) } if len(inputs) == 0 { return nil } - if r, _, err := sendInputProc.Call(uintptr(len(inputs)), - uintptr(unsafe.Pointer(&inputs[0])), - unsafe.Sizeof(inputs[0])); int(r) != len(inputs) { + if sent, _, err := sendInputProc.Call(uintptr(len(inputs)), + uintptr(unsafe.Pointer(&inputs[0])), unsafe.Sizeof(inputs[0])); int(sent) != len(inputs) { return err } return nil } - -func (p *windowsBackend) PointerScrollFinish() error { - return nil -} diff --git a/backend_x11.go b/inputcontrol/controller_x11.go similarity index 50% rename from backend_x11.go rename to inputcontrol/controller_x11.go index ad3445b..f45b75b 100644 --- a/backend_x11.go +++ b/inputcontrol/controller_x11.go @@ -1,4 +1,4 @@ -// +build x11 +//go:build x11 /* * Copyright (c) 2018 Unrud @@ -19,52 +19,101 @@ * along with Remote-Touchpad. If not, see . */ -package main +package inputcontrol -// #cgo LDFLAGS: -lX11 -lXtst +// #cgo LDFLAGS: -lX11 -lXrandr -lXtst +// #include // #include // #include +// #include // #include // #include +// Window MacroDefaultRootWindow(Display *dpy) { +// return DefaultRootWindow(dpy); +// } import "C" + import ( "errors" "fmt" - "os" + "maps" + "slices" + "strings" "sync" "time" "unsafe" ) const ( - keyboardMappingDelay time.Duration = 500 * time.Millisecond + keyboardMappingDelay time.Duration = 200 * time.Millisecond scrollDiv int = 20 ) -var modifierIndices [6]uint = [...]uint{C.ShiftMapIndex, C.Mod1MapIndex, - C.Mod2MapIndex, C.Mod3MapIndex, C.Mod4MapIndex, C.Mod5MapIndex} +var modifierIndexes = [...]int{ + C.ShiftMapIndex, + C.Mod1MapIndex, + C.Mod2MapIndex, + C.Mod3MapIndex, + C.Mod4MapIndex, + C.Mod5MapIndex, +} -type x11Backend struct { +type x11Controller struct { display *C.Display lock sync.Mutex scrollHorizontal, scrollVertical int } -func InitX11Backend() (Backend, error) { - sessionType := os.Getenv("XDG_SESSION_TYPE") - if sessionType != "" && sessionType != "x11" { - return nil, UnsupportedPlatformError{errors.New(fmt.Sprintf( - "unsupported session type '%v'", sessionType))} - } - display := C.XOpenDisplay(nil) - if display == nil { - return nil, UnsupportedPlatformError{ - errors.New("failed to connect to X server")} - } - return &x11Backend{display: display}, nil +func init() { + RegisterController("X11", InitX11Controller, 0) } -func (p *x11Backend) Close() error { +func InitX11Controller() (Controller, error) { + display := C.XOpenDisplay(nil) + if display == nil { + return nil, &UnsupportedPlatformError{ + errors.New("failed to connect to X server"), + } + } + p := &x11Controller{display: display} + if p.xIsXwayland() { + p.Close() + return nil, &UnsupportedPlatformError{ + errors.New("X server is Xwayland"), + } + } + return p, nil +} + +func (p *x11Controller) xIsXwayland() bool { + // Detection method from https://gitlab.freedesktop.org/xorg/app/xisxwayland/-/blob/xisxwayland-2/xisxwayland.c + var opcode, event, error, major, minor C.int + xwaylandExtensionName := C.CString("XWAYLAND") + defer C.free(unsafe.Pointer(xwaylandExtensionName)) + if C.XQueryExtension(p.display, xwaylandExtensionName, &opcode, &event, &error) != 0 { + return true + } + if C.XRRQueryExtension(p.display, &event, &error) == 0 || + C.XRRQueryVersion(p.display, &major, &minor) == 0 { + return false + } + resources := C.XRRGetScreenResourcesCurrent(p.display, C.MacroDefaultRootWindow(p.display)) + if resources == nil { + return false + } + defer C.XRRFreeScreenResources(resources) + if resources.noutput < 1 { + return false + } + output := C.XRRGetOutputInfo(p.display, resources, *resources.outputs) + if output == nil { + return false + } + defer C.XRRFreeOutputInfo(output) + return strings.HasPrefix(C.GoString(output.name), "XWAYLAND") +} + +func (p *x11Controller) Close() error { p.lock.Lock() defer p.lock.Unlock() if p.display == nil { @@ -75,53 +124,68 @@ func (p *x11Backend) Close() error { return nil } -func (p *x11Backend) findEmptyKeycodeLocked() (C.KeyCode, C.int, error) { +func (p *x11Controller) findEmptyKeycodeLocked() (keycode C.KeyCode, keysymsPerKeycode int, err error) { var minKeycodes, maxKeycodes C.int C.XDisplayKeycodes(p.display, &minKeycodes, &maxKeycodes) - var keysymsPerKeycode C.int - keysyms := C.XGetKeyboardMapping(p.display, C.KeyCode(minKeycodes), - maxKeycodes-minKeycodes+1, &keysymsPerKeycode) + var keysymsPerKeycodeC C.int + keysyms := C.XGetKeyboardMapping( + p.display, + C.KeyCode(minKeycodes), + maxKeycodes-minKeycodes+1, + &keysymsPerKeycodeC, + ) if keysyms == nil { return 0, 0, errors.New("failed to get keyboard mapping") } defer C.XFree(unsafe.Pointer(keysyms)) + keysymsPerKeycode = int(keysymsPerKeycodeC) keycodes: - for keycode := C.KeyCode(minKeycodes); keycode <= C.KeyCode(maxKeycodes); keycode++ { - for i := 0; i < int(keysymsPerKeycode); i++ { - keysymsIndex := int(keycode- - C.KeyCode(minKeycodes))*int(keysymsPerKeycode) + i - keysym := *(*C.KeySym)(unsafe.Pointer(uintptr(unsafe.Pointer(keysyms)) + - uintptr(keysymsIndex)*unsafe.Sizeof(*keysyms))) + for keycode := int(minKeycodes); keycode <= int(maxKeycodes); keycode++ { + for i := range keysymsPerKeycode { + keysymsIndex := (keycode-int(minKeycodes))*keysymsPerKeycode + i + keysym := *(*C.KeySym)(unsafe.Pointer( + uintptr(unsafe.Pointer(keysyms)) + + uintptr(keysymsIndex)*unsafe.Sizeof(*keysyms), + )) if keysym != 0 { continue keycodes } } - return keycode, keysymsPerKeycode, nil + return C.KeyCode(keycode), keysymsPerKeycode, nil } return 0, 0, errors.New("no empty keycode found") } -func (p *x11Backend) changeKeyMappingLocked(keysymsPerKeycode C.int, - keycode C.KeyCode, keysym Keysym) { +func (p *x11Controller) changeKeyMappingLocked( + keycode C.KeyCode, keysymsPerKeycode int, keysym Keysym, +) { keycodeMapping := make([]C.KeySym, keysymsPerKeycode) for i := range keycodeMapping { keycodeMapping[i] = C.KeySym(keysym) } - C.XChangeKeyboardMapping(p.display, C.int(keycode), keysymsPerKeycode, - (*C.KeySym)(unsafe.Pointer(&keycodeMapping[0])), 1) - C.XFlush(p.display) + C.XChangeKeyboardMapping( + p.display, + C.int(keycode), + C.int(keysymsPerKeycode), + (*C.KeySym)(unsafe.Pointer(&keycodeMapping[0])), + 1, + ) + C.XSync(p.display, C.False) } -func (p *x11Backend) getModKeycodesLocked() map[uint]C.KeyCode { +func (p *x11Controller) getModKeycodesLocked() map[uint]C.KeyCode { modKeymap := C.XGetModifierMapping(p.display) defer C.XFreeModifiermap(modKeymap) modKeycodes := make(map[uint]C.KeyCode) - for _, modIndex := range modifierIndices { - for i := 0; i < int(modKeymap.max_keypermod); i++ { - keycode := *(*C.KeyCode)(unsafe.Pointer(uintptr(unsafe.Pointer(modKeymap.modifiermap)) + - uintptr(uint(modIndex)*uint(modKeymap.max_keypermod)+uint(i)))) + for _, modIndex := range modifierIndexes { + for i := range int(modKeymap.max_keypermod) { + keycodeIndex := modIndex*int(modKeymap.max_keypermod) + i + keycode := *(*C.KeyCode)(unsafe.Pointer( + uintptr(unsafe.Pointer(modKeymap.modifiermap)) + + uintptr(keycodeIndex)*unsafe.Sizeof(*modKeymap.modifiermap), + )) if keycode != 0 { - modKeycodes[1< + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +package inputcontrol + +import ( + "fmt" + "os/exec" + "strings" + "unsafe" +) + +const bkeymapFileSignature = "bkeymap" + +// uapi/linux/keyboard.h +const ( + nrKeys = 128 + maxNrKeymaps = 256 + nrShift = 9 + + ktLatin = 0 + ktShift = 7 + ktLetter = 11 +) + +type KeyCombo struct { + Key int + ShiftKeys [nrShift]int +} + +type Keymap struct { + keyComboMap map[rune]KeyCombo +} + +func (keymap Keymap) Get(character rune) (keyCombo KeyCombo, found bool) { + keyCombo, found = keymap.keyComboMap[character] + return +} + +func LoadKeymap(name string) (*Keymap, error) { + if len(name) == 0 || strings.HasPrefix(name, "-") { + return nil, fmt.Errorf("invalid keymap name: %#v", name) + } + bkeymap, err := exec.Command("loadkeys", "--bkeymap", name).Output() + if err != nil { + return nil, &UnsupportedPlatformError{err} + } + if !strings.HasPrefix(string(bkeymap), bkeymapFileSignature) { + return nil, fmt.Errorf("invalid bkeymap: signature not found") + } + bkeymapHeader := bkeymap[len(bkeymapFileSignature):] + var keymaps [][nrKeys]int + if len(bkeymapHeader) < maxNrKeymaps { + return nil, fmt.Errorf("invalid bkeymap: EOF") + } + currentBkeymap := bkeymapHeader[maxNrKeymaps:] + for keymapIndex := 0; keymapIndex < maxNrKeymaps; keymapIndex += 1 { + if bkeymapHeader[keymapIndex] == 0 { + continue + } + if len(currentBkeymap) < nrKeys*2 { + return nil, fmt.Errorf("invalid bkeymap: EOF") + } + for len(keymaps) <= keymapIndex { + keymaps = append(keymaps, [nrKeys]int{}) + } + for key := 0; key < nrKeys; key += 1 { + var keysym uint16 + copy(unsafe.Slice((*byte)(unsafe.Pointer(&keysym)), 2), + currentBkeymap[key*2:]) + keymaps[keymapIndex][key] = int(keysym) + } + currentBkeymap = currentBkeymap[nrKeys*2:] + } + keymap := Keymap{keyComboMap: make(map[rune]KeyCombo)} + if len(keymaps) == 0 { + return &keymap, nil + } + var shiftKeys [nrShift]int + for key, keysym := range keymaps[0] { + keyType := keysym >> 8 + keyValue := keysym & 0xff + if keyType == ktShift && keyValue < nrShift && shiftKeys[keyValue] == 0 { + shiftKeys[keyValue] = key + } + } +keymapLoop: + for keymapIndex := range keymaps { + var activeShiftKeys [nrShift]int + for i, shiftKey := range shiftKeys { + if keymapIndex&(1<> 8 + keyValue := keysym & 0xff + if (keyType != ktLatin && keyType != ktLetter) || keyValue == 0 { + continue + } + character := rune(keyValue) + if _, ok := keymap.keyComboMap[character]; ok { + continue + } + keymap.keyComboMap[character] = KeyCombo{key, activeShiftKeys} + } + } + return &keymap, nil +} diff --git a/keysyms.generated.go b/inputcontrol/keysyms.generated.go similarity index 98% rename from keysyms.generated.go rename to inputcontrol/keysyms.generated.go index 1e36558..8734718 100644 --- a/keysyms.generated.go +++ b/inputcontrol/keysyms.generated.go @@ -1,7 +1,7 @@ // Code generated by keysyms.generator.go. DO NOT EDIT. -// +build portal x11 +//go:build portal || x11 -package main +package inputcontrol var keysymsMap = map[rune]Keysym{ 0x0008: 0x0000ff08, diff --git a/keysyms.generator.go b/inputcontrol/keysyms.generator.go similarity index 97% rename from keysyms.generator.go rename to inputcontrol/keysyms.generator.go index 182e5ed..cc18417 100644 --- a/keysyms.generator.go +++ b/inputcontrol/keysyms.generator.go @@ -1,4 +1,4 @@ -// +build ignore +//go:build ignore /* * Copyright (c) 2018 Unrud @@ -102,8 +102,8 @@ func main() { keysymsMap[unicode] = keysym } content := "// Code generated by keysyms.generator.go. DO NOT EDIT.\n" + - "// +build portal x11\n\n" + - "package main\n\n" + + "//go:build portal || x11\n\n" + + "package inputcontrol\n\n" + "var keysymsMap = map[rune]Keysym{\n" keys := make([]rune, 0) for key := range keysymsMap { diff --git a/keysyms.go b/inputcontrol/keysyms.go similarity index 62% rename from keysyms.go rename to inputcontrol/keysyms.go index 61c26ec..de183a1 100644 --- a/keysyms.go +++ b/inputcontrol/keysyms.go @@ -1,4 +1,4 @@ -// +build portal x11 +//go:build portal || x11 /* * Copyright (c) 2018 Unrud @@ -19,17 +19,26 @@ * along with Remote-Touchpad. If not, see . */ -package main +package inputcontrol //go:generate go run keysyms.generator.go -import "errors" +import "fmt" type Keysym int32 const ( // X11/keysymdef.h - xkSuperL Keysym = 0xffeb + xkBackSpace Keysym = 0xff08 + xkReturn Keysym = 0xff0D + xkDelete Keysym = 0xffff + xkHome Keysym = 0xff50 + xkLeft Keysym = 0xff51 + xkUp Keysym = 0xff52 + xkRight Keysym = 0xff53 + xkDown Keysym = 0xff54 + xkEnd Keysym = 0xff57 + xkSuperL Keysym = 0xffeb // X11/XF86keysym.h xf86xkAudioLowerVolume Keysym = 0x1008ff11 xf86xkAudioMute Keysym = 0x1008ff12 @@ -48,8 +57,8 @@ func RuneToKeysym(runeValue rune) (Keysym, error) { keysym, found := keysymsMap[runeValue] if !found { if runeValue < 0x100 || runeValue > 0x10ffff { - return 0, errors.New("rune not mappend to keysym and " + - "out of range for direct unicode mapping") + return 0, fmt.Errorf("rune not mappend to keysym and "+ + "out of range for direct unicode mapping: %q", runeValue) } keysym = Keysym(0x01000000 + runeValue) } @@ -57,32 +66,44 @@ func RuneToKeysym(runeValue rune) (Keysym, error) { } func KeyToKeysym(key Key) (Keysym, error) { - if key == KeySuper { + switch key { + case KeyBackSpace: + return xkBackSpace, nil + case KeyReturn: + return xkReturn, nil + case KeyDelete: + return xkDelete, nil + case KeyHome: + return xkHome, nil + case KeyLeft: + return xkLeft, nil + case KeyUp: + return xkUp, nil + case KeyRight: + return xkRight, nil + case KeyDown: + return xkDown, nil + case KeyEnd: + return xkEnd, nil + case KeySuper: return xkSuperL, nil - } - if key == KeyVolumeMute { + case KeyVolumeMute: return xf86xkAudioMute, nil - } - if key == KeyVolumeDown { + case KeyVolumeDown: return xf86xkAudioLowerVolume, nil - } - if key == KeyVolumeUp { + case KeyVolumeUp: return xf86xkAudioRaiseVolume, nil - } - if key == KeyMediaPlayPause { + case KeyMediaPlayPause: return xf86xkAudioPlay, nil - } - if key == KeyMediaPrevTrack { + case KeyMediaPrevTrack: return xf86xkAudioPrev, nil - } - if key == KeyMediaNextTrack { + case KeyMediaNextTrack: return xf86xkAudioNext, nil - } - if key == KeyBrowserBack { + case KeyBrowserBack: return xf86xkBack, nil - } - if key == KeyBrowserForward { + case KeyBrowserForward: return xf86xkForward, nil + default: + return 0, fmt.Errorf("key not mapped to keysym: %#v", key) } - return 0, errors.New("key not mapped to keysym") } diff --git a/main.go b/main.go index 5ba39ae..f8db693 100644 --- a/main.go +++ b/main.go @@ -27,7 +27,6 @@ import ( "errors" "flag" "fmt" - "golang.org/x/net/websocket" "log" mathrand "math/rand" "net" @@ -37,6 +36,10 @@ import ( "strings" "time" "unicode/utf8" + + "github.com/unrud/remote-touchpad/inputcontrol" + "github.com/unrud/remote-touchpad/terminal" + "golang.org/x/net/websocket" ) const ( @@ -45,26 +48,31 @@ const ( authenticationRateBurst int = 10 challengeLength int = 8 defaultBind string = ":0" - version string = "1.0.3" - pretty_app_name string = "Remote Touchpad" + version string = "1.5.4" + prettyAppName string = "Remote Touchpad" ) -func processCommand(backend Backend, command string) error { +type config struct { + UpdateRate uint `json:"updateRate"` + ScrollSpeed float64 `json:"scrollSpeed"` + MoveSpeed float64 `json:"moveSpeed"` + MouseScrollSpeed float64 `json:"mouseScrollSpeed"` + MouseMoveSpeed float64 `json:"mouseMoveSpeed"` +} + +func processCommand(controller inputcontrol.Controller, command string) error { if len(command) == 0 { return errors.New("empty command") } - if command == "sf" { - return backend.PointerScrollFinish() + if command == "S" { + return controller.PointerScroll(0, 0, true) } if command[0] == 't' { text := command[1:] - // normalize line endings - text = strings.Replace(text, "\r\n", "\n", -1) - text = strings.Replace(text, "\r", "\n", -1) if !utf8.ValidString(text) { return errors.New("invalid utf-8") } - return backend.KeyboardText(text) + return controller.KeyboardText(text) } arguments := strings.Split(command[1:], ";") if command[0] == 'k' && len(arguments) != 1 || @@ -76,30 +84,33 @@ func processCommand(backend Backend, command string) error { return err } if command[0] == 'k' { - if x < 0 || x >= int64(KeyLimit) { + if x < 0 || x >= int64(inputcontrol.KeyLimit) { return errors.New("unsupported key") } - return backend.KeyboardKey(Key(x)) + return controller.KeyboardKey(inputcontrol.Key(x)) } y, err := strconv.ParseInt(arguments[1], 10, 32) if err != nil { return err } if command[0] == 'm' { - return backend.PointerMove(int(x), int(y)) + return controller.PointerMove(int(x), int(y)) } if command[0] == 's' { - return backend.PointerScroll(int(x), int(y)) + return controller.PointerScroll(int(x), int(y), false) + } + if command[0] == 'S' { + return controller.PointerScroll(int(x), int(y), true) } if command[0] == 'b' { - if x < 0 || x >= int64(PointerButtonLimit) { + if x < 0 || x >= int64(inputcontrol.PointerButtonLimit) { return errors.New("unsupported pointer button") } b := true if y == 0 { b = false } - return backend.PointerButton(PointerButton(x), b) + return controller.PointerButton(inputcontrol.PointerButton(x), b) } return errors.New("unsupported command") } @@ -140,14 +151,20 @@ func secureRandBase64(length int) string { } func main() { - TerminalSetTitle(pretty_app_name) + terminal.SetTitle(prettyAppName) var bind, certFile, keyFile, secret string var showVersion bool + var config config flag.BoolVar(&showVersion, "version", false, "show program's version number and exit") flag.StringVar(&bind, "bind", defaultBind, "bind server to [HOSTNAME]:PORT") flag.StringVar(&secret, "secret", "", "shared secret for client authentication") flag.StringVar(&certFile, "cert", "", "file containing TLS certificate") flag.StringVar(&keyFile, "key", "", "file containing TLS private key") + flag.UintVar(&config.UpdateRate, "update-rate", 30, "number of updates per second") + flag.Float64Var(&config.MoveSpeed, "move-speed", 1, "move speed multiplier") + flag.Float64Var(&config.ScrollSpeed, "scroll-speed", 1, "scroll speed multiplier") + flag.Float64Var(&config.MouseMoveSpeed, "mouse-move-speed", 1, "mouse move speed multiplier") + flag.Float64Var(&config.MouseScrollSpeed, "mouse-scroll-speed", 1, "mouse scroll speed multiplier") flag.Parse() if showVersion { fmt.Println(version) @@ -163,25 +180,32 @@ func main() { if secret == "" { secret = secureRandBase64(defaultSecretLength) } - var backend Backend - var backendName string - platformErrors := "" - for _, backendInfo := range Backends { - backendName = backendInfo.Name + if len(inputcontrol.Controllers) == 0 { + log.Fatal("compiled without controller") + } + var controller inputcontrol.Controller + var controllerName string + var platformErrs []error + for _, controllerInfo := range inputcontrol.Controllers { + controllerName = controllerInfo.Name var err error - backend, err = backendInfo.Init() + controller, err = controllerInfo.Init() if err == nil { break - } else if _, ok := err.(UnsupportedPlatformError); ok { - platformErrors += fmt.Sprintf("%s backend: %v\n", backendName, err) } else { - log.Fatal(fmt.Sprintf("%s backend: %v", backendName, err)) + var unsupportedErr *inputcontrol.UnsupportedPlatformError + wrappedErr := fmt.Errorf("%v controller: %w", controllerName, err) + if errors.As(err, &unsupportedErr) { + platformErrs = append(platformErrs, wrappedErr) + } else { + log.Fatal(wrappedErr) + } } } - if backend == nil { - log.Fatal("unsupported platform:\n" + platformErrors) + if controller == nil { + log.Fatal(fmt.Errorf("unsupported platform:\n%w", errors.Join(platformErrs...))) } - defer backend.Close() + defer controller.Close() authenticationChallenges := make(chan challenge, authenticationRateBurst) go authenticationChallengeGenerator(secret, authenticationChallenges) listener, err := net.Listen("tcp", bind) @@ -201,11 +225,11 @@ func main() { } } if host == "" { - host = FindDefaultHost() + host = findDefaultHost() } port := addr.Port mux := http.NewServeMux() - mux.Handle("/", http.FileServer(assetFS())) + mux.Handle("/", http.FileServer(http.FS(webdataFS))) mux.Handle("/ws", websocket.Handler(func(ws *websocket.Conn) { var message string challenge := <-authenticationChallenges @@ -216,12 +240,13 @@ func main() { if !challenge.verify(message) { return } + websocket.JSON.Send(ws, config) for { if err := websocket.Message.Receive(ws, &message); err != nil { return } - if err := processCommand(backend, message); err != nil { - log.Print(fmt.Sprintf("%s backend: %v", backendName, err)) + if err := processCommand(controller, message); err != nil { + log.Print(fmt.Errorf("%s controller: %w", controllerName, err)) return } } @@ -234,16 +259,17 @@ func main() { if tls { scheme = "https" } - url := fmt.Sprintf("%s://%s/#%s\n", scheme, domain, secret) - readyMsg := "ready: " + url - qrCode, _ := GenerateQRCode(url, TerminalSupportsColor(os.Stderr.Fd())) - readyMsg += qrCode - if !tls { - readyMsg += ("" + - "▌ WARNING: TLS is not enabled ▐\n" + - "▌Don't use in an untrusted network!▐\n") + url := fmt.Sprintf("%s://%s/#%s", scheme, domain, secret) + fmt.Println(url) + if qrCode, err := terminal.GenerateQRCode(url, terminal.SupportsColor(os.Stdout.Fd())); err == nil { + fmt.Print(qrCode) + } else { + log.Printf("QR code error: %v", err) + } + if !tls { + fmt.Println("▌ WARNING: TLS is not enabled ▐") + fmt.Println("▌Don't use in an untrusted network!▐") } - log.Print(readyMsg) if tls { err = http.ServeTLS(listener, mux, certFile, keyFile) } else { diff --git a/remote-touchpad.service b/remote-touchpad.service new file mode 100644 index 0000000..76f5a74 --- /dev/null +++ b/remote-touchpad.service @@ -0,0 +1,12 @@ +[Unit] +Description=Remote Touchpad Service +After=network.target + +[Service] +Type=simple +ExecStart=%h/.local/bin/remote-touchpad +Restart=always +RestartSec=5s + +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/rts b/rts new file mode 100755 index 0000000..84fbe46 --- /dev/null +++ b/rts @@ -0,0 +1,149 @@ +#!/bin/bash + +# Remote Touchpad CLI Wrapper +# Provides easy access to remote-touchpad service + +SERVICE_NAME="remote-touchpad" +SERVICE_USER="$USER" + +install() { + echo "Installing remote-touchpad as a user systemd service..." + + # Check if Go is installed + if ! command -v go &> /dev/null; then + echo "Error: Go is not installed. Please install Go first." + return 1 + fi + + echo "Building remote-touchpad binary..." + go build -tags portal,uinput,x11 -o remote-touchpad + + if [ ! -f "remote-touchpad" ]; then + echo "Error: Failed to build remote-touchpad binary" + return 1 + fi + + # Setup directories + mkdir -p ~/.local/bin ~/.config/systemd/user + + echo "Installing service files..." + cp remote-touchpad ~/.local/bin/ || echo "Error: cp to ~/.local/bin failed" + cp remote-touchpad.service ~/.config/systemd/user/ || echo "Error: cp to ~/.config/systemd/user failed" + + echo "Enabling and starting service..." + systemctl --user daemon-reload + systemctl --user enable --now ${SERVICE_NAME}.service + + echo "✓ remote-touchpad user service installed and started" + echo "" + echo "Use './rts status' to check the service" + echo "Use './rts url' to get the connection URL" +} + +start() { + systemctl --user start ${SERVICE_NAME}.service + echo "Started remote-touchpad service" +} + +stop() { + systemctl --user stop ${SERVICE_NAME}.service + echo "Stopped remote-touchpad service" +} + +status() { + systemctl --user status ${SERVICE_NAME}.service --no-pager +} + +restart() { + systemctl --user restart ${SERVICE_NAME}.service + echo "Restarted remote-touchpad service" +} + +url() { + # Extract URL from service logs + echo "Fetching remote-touchpad URL..." + local url=$(journalctl --user -u ${SERVICE_NAME}.service -n 100 --no-pager 2>/dev/null | grep -oE "http://[^ ]+|https://[^ ]+" | tail -1) + if [ -n "$url" ]; then + echo "Remote Touchpad URL: $url" + # Generate QR code if qrencode is available + if command -v qrencode &> /dev/null; then + echo "Generating QR code..." + echo "$url" | qrencode -t ansiutf8 + else + echo "QR code generation requires qrencode. Install it with:" + echo " sudo apt-get install qrencode # Debian/Ubuntu" + echo " sudo dnf install qrencode # Fedora" + echo " brew install qrencode # macOS" + fi + else + echo "Could not find URL in service logs. Is the service running?" + fi +} + +log() { + echo "Viewing remote-touchpad service logs..." + journalctl --user -u ${SERVICE_NAME}.service --no-pager +} + +uninstall() { + echo "Uninstalling remote-touchpad..." + systemctl --user stop ${SERVICE_NAME}.service + systemctl --user disable ${SERVICE_NAME}.service + rm -f ~/.config/systemd/user/${SERVICE_NAME}.service + rm -f ~/.local/bin/remote-touchpad + systemctl --user daemon-reload + echo "✓ remote-touchpad uninstalled" +} + +help() { + echo "Remote Touchpad CLI Wrapper" + echo "" + echo "Usage: rts [command]" + echo "" + echo "Commands:" + echo " install - Install remote-touchpad as a systemd user service" + echo " start - Start the remote-touchpad service" + echo " stop - Stop the remote-touchpad service" + echo " status - Check service status" + echo " restart - Restart the service" + echo " url - Show the remote-touchpad URL" + echo " log - View service logs" + echo " uninstall - Uninstall the service" + echo " help - Show this help message" +} + +# Main command handler +case "$1" in + install) + install + ;; + start) + start + ;; + stop) + stop + ;; + status) + status + ;; + restart) + restart + ;; + url) + url + ;; + log) + log + ;; + uninstall) + uninstall + ;; + help|--help|-h|"") + help + ;; + *) + echo "Unknown command: $1" + help + exit 1 + ;; +esac diff --git a/screenshots/1.png b/screenshots/1.png index 798a89d..a85086a 100644 Binary files a/screenshots/1.png and b/screenshots/1.png differ diff --git a/screenshots/2.png b/screenshots/2.png index f647fbf..58922a3 100644 Binary files a/screenshots/2.png and b/screenshots/2.png differ diff --git a/screenshots/3.png b/screenshots/3.png index 01008ca..43bff6b 100644 Binary files a/screenshots/3.png and b/screenshots/3.png differ diff --git a/screenshots/4.png b/screenshots/4.png index a1ffe07..6fceade 100644 Binary files a/screenshots/4.png and b/screenshots/4.png differ diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index f32c543..6d23326 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,17 +1,21 @@ -# Build by running "snapcraft". +# Build by running `snapcraft`. # WARNING: # Snapcraft uses caching for already build steps but it's buggy and can cause strange problems. -# Clean the cache by running "snapcraft clean". +# Clean the cache by running `snapcraft clean`. name: remote-touchpad license: GPL-3.0+ grade: stable adopt-info: remote-touchpad -base: core18 +base: core24 confinement: strict +platforms: + amd64: + arm64: + apps: remote-touchpad: command: bin/remote-touchpad @@ -20,13 +24,13 @@ apps: - desktop - x11 remote-touchpad-wait-on-error: - command: usr/bin/remote-touchpad-wait-on-error + command: bin/remote-touchpad-wait-on-error plugs: - network-bind - desktop - x11 common-id: com.github.unrud.RemoteTouchpad - desktop: share/applications/com.github.unrud.RemoteTouchpad.desktop + desktop: usr/share/applications/com.github.unrud.RemoteTouchpad.desktop parts: remote-touchpad: @@ -36,18 +40,22 @@ parts: - x11 source: . source-type: git + build-snaps: + - go build-packages: - gcc - libc6-dev - libxt-dev + - libxrandr-dev - libxtst-dev stage-packages: - libxt6 + - libxrandr2 - libxtst6 override-build: | - snapcraftctl build - install -Dm0755 -t "${SNAPCRAFT_PART_INSTALL}/usr/bin" desktop/remote-touchpad-wait-on-error - install -Dm0644 -t "${SNAPCRAFT_PART_INSTALL}/usr/share/metainfo" desktop/com.github.unrud.RemoteTouchpad.metainfo.xml - install -Dm0644 -t "${SNAPCRAFT_PART_INSTALL}/usr/share/applications" desktop/com.github.unrud.RemoteTouchpad.desktop - install -Dm0644 -t "${SNAPCRAFT_PART_INSTALL}/usr/share/icons/hicolor/scalable/apps" desktop/com.github.unrud.RemoteTouchpad.svg + craftctl default + install -Dm0755 -t "${CRAFT_PART_INSTALL}/bin" desktop/remote-touchpad-wait-on-error + install -Dm0644 -t "${CRAFT_PART_INSTALL}/usr/share/metainfo" desktop/com.github.unrud.RemoteTouchpad.metainfo.xml + install -Dm0644 -t "${CRAFT_PART_INSTALL}/usr/share/applications" desktop/com.github.unrud.RemoteTouchpad.desktop + install -Dm0644 -t "${CRAFT_PART_INSTALL}/usr/share/icons/hicolor/scalable/apps" desktop/com.github.unrud.RemoteTouchpad.svg parse-info: [usr/share/metainfo/com.github.unrud.RemoteTouchpad.metainfo.xml] diff --git a/qr.go b/terminal/qr.go similarity index 92% rename from qr.go rename to terminal/qr.go index aae21db..105aee7 100644 --- a/qr.go +++ b/terminal/qr.go @@ -17,7 +17,7 @@ * along with Remote-Touchpad. If not, see . */ -package main +package terminal import qrcode "github.com/skip2/go-qrcode" @@ -33,7 +33,7 @@ func qrCodeToString(bits [][]bool, colorize bool) string { s := "" for y := -1; y < len(bits); y += 2 { if colorize { - s += TerminalForegroundWhite + TerminalBackgroundBlack + s += ForegroundBlack + BackgroundWhite } for x := range bits[0] { upper := false @@ -45,17 +45,17 @@ func qrCodeToString(bits [][]bool, colorize bool) string { lower = bits[y+1][x] } if upper && lower { - s += " " - } else if !upper && lower { - s += "▀" + s += "█" } else if upper && !lower { + s += "▀" + } else if !upper && lower { s += "▄" } else { - s += "█" + s += " " } } if colorize { - s += TerminalForegroundReset + TerminalBackgroundReset + s += ForegroundReset + BackgroundReset } s += "\n" } diff --git a/terminal.go b/terminal/terminal.go similarity index 80% rename from terminal.go rename to terminal/terminal.go index 91de022..8050fc6 100644 --- a/terminal.go +++ b/terminal/terminal.go @@ -17,11 +17,11 @@ * along with Remote-Touchpad. If not, see . */ -package main +package terminal const ( - TerminalForegroundWhite string = "\033[37m" - TerminalForegroundReset string = "\033[39m" - TerminalBackgroundBlack string = "\033[40m" - TerminalBackgroundReset string = "\033[49m" + ForegroundBlack string = "\033[30m" + ForegroundReset string = "\033[39m" + BackgroundWhite string = "\033[47m" + BackgroundReset string = "\033[49m" ) diff --git a/terminal_posix.go b/terminal/terminal_posix.go similarity index 89% rename from terminal_posix.go rename to terminal/terminal_posix.go index e79ea61..9d80e70 100644 --- a/terminal_posix.go +++ b/terminal/terminal_posix.go @@ -1,4 +1,4 @@ -// +build !windows +//go:build !windows /* * Copyright (c) 2018-2019 Unrud @@ -19,17 +19,17 @@ * along with Remote-Touchpad. If not, see . */ -package main +package terminal // #include import "C" import "os" -func TerminalSupportsColor(fd uintptr) bool { +func SupportsColor(fd uintptr) bool { return C.isatty(C.int(fd)) != 0 } -func TerminalSetTitle(title string) bool { +func SetTitle(title string) bool { if C.isatty(C.int(os.Stdout.Fd())) != 0 { os.Stdout.Write([]byte("\x1b]2;" + title + "\x07")) os.Stdout.Sync() diff --git a/terminal_windows.go b/terminal/terminal_windows.go similarity index 93% rename from terminal_windows.go rename to terminal/terminal_windows.go index 5637a7d..b28c084 100644 --- a/terminal_windows.go +++ b/terminal/terminal_windows.go @@ -17,7 +17,7 @@ * along with Remote-Touchpad. If not, see . */ -package main +package terminal import ( "syscall" @@ -36,13 +36,13 @@ var ( setConsoleTitleProc = kernel32DLL.NewProc("SetConsoleTitleW") ) -func TerminalSupportsColor(fd uintptr) bool { +func SupportsColor(fd uintptr) bool { r, _, _ := setConsoleModeProc.Call(fd, uintptr(enableProcessedOuput| enableWrapAtEolOutput|enableVirtualTerminalProcessing)) return r != 0 } -func TerminalSetTitle(title string) bool { +func SetTitle(title string) bool { r, _, _ := setConsoleTitleProc.Call( uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title)))) return r != 0 diff --git a/backend_portal_disabled.go b/webdata.go similarity index 55% rename from backend_portal_disabled.go rename to webdata.go index 05ca72f..1b816b6 100644 --- a/backend_portal_disabled.go +++ b/webdata.go @@ -1,7 +1,5 @@ -// +build !portal - /* - * Copyright (c) 2018 Unrud + * Copyright (c) 2024 Unrud * * This file is part of Remote-Touchpad. * @@ -21,8 +19,34 @@ package main -import "errors" +import ( + "embed" + "io/fs" + "log" + "mime" +) -func InitPortalBackend() (Backend, error) { - return nil, UnsupportedPlatformError{errors.New("disabled")} +//go:embed webdata/* +var webdataFSWithPrefix embed.FS +var webdataFS fs.FS + +var webdataTypes = map[string]string{ + ".css": "text/css; charset=utf-8", + ".html": "text/html; charset=utf-8", + ".mjs": "text/javascript; charset=utf-8", + ".png": "image/png", + ".woff": "font/woff", +} + +func init() { + var err error + webdataFS, err = fs.Sub(webdataFSWithPrefix, "webdata") + if err != nil { + log.Fatal(err) + } + for ext, typ := range webdataTypes { + if err := mime.AddExtensionType(ext, typ); err != nil { + log.Fatal(err) + } + } } diff --git a/webdata/app/compat.mjs b/webdata/app/compat.mjs new file mode 100644 index 0000000..8a26833 --- /dev/null +++ b/webdata/app/compat.mjs @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +export const fullscreenEnabled = () => { + return (document.fullscreenEnabled || + document.webkitFullscreenEnabled || + false); +}; + +export const requestFullscreen = (element, options) => { + if (element.requestFullscreen) { + element.requestFullscreen(options); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(options); + } +}; + +export const exitFullscreen = () => { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } +}; + +export const fullscreenElement = () => { + return (document.fullscreenElement || + document.webkitFullscreenElement || + null); +}; + +export const addFullscreenchangeEventListener = (listener) => { + if ("onfullscreenchange" in document) { + document.addEventListener("fullscreenchange", listener); + } else if ("onwebkitfullscreenchange" in document) { + document.addEventListener("webkitfullscreenchange", listener); + } +}; + +export const requestPointerLock = (element) => { + if (element.requestPointerLock) { + element.requestPointerLock(); + } +}; + +export const exitPointerLock = () => { + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}; + +export const pointerLockElement = () => { + return document.pointerLockElement || null; +}; + +export const addPointerlockchangeEventListener = (listener) => { + if ("onpointerlockchange" in document) { + document.addEventListener("pointerlockchange", listener); + } +}; + +export const vibrate = (pattern) => { + if (navigator.vibrate) { + navigator.vibrate(pattern); + } +}; diff --git a/webdata/app/inputcontroller.mjs b/webdata/app/inputcontroller.mjs new file mode 100644 index 0000000..a2984fb --- /dev/null +++ b/webdata/app/inputcontroller.mjs @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +export const POINTER_BUTTON_LEFT = 0; +export const POINTER_BUTTON_RIGHT = 1; +export const POINTER_BUTTON_MIDDLE = 2; + +export const KEY_VOLUME_MUTE = 0; +export const KEY_VOLUME_DOWN = 1; +export const KEY_VOLUME_UP = 2; +export const KEY_MEDIA_PLAY_PAUSE = 3; +export const KEY_MEDIA_PREV_TRACK = 4; +export const KEY_MEDIA_NEXT_TRACK = 5; +export const KEY_BROWSER_BACK = 6; +export const KEY_BROWSER_FORWARD = 7; +export const KEY_SUPER = 8; +export const KEY_LEFT = 9; +export const KEY_RIGHT = 10; +export const KEY_UP = 11; +export const KEY_DOWN = 12; +export const KEY_HOME = 13; +export const KEY_END = 14; +export const KEY_BACK_SPACE = 15; +export const KEY_DELETE = 16; +export const KEY_RETURN = 17; + +export default class InputController { + #updateRate = 0; + + #moveXSum = 0; + #moveYSum = 0; + #scrollHSum = 0; + #scrollVSum = 0; + #scrolling = false; + #scrollFinish = false; + #updateTimeoutActive = false; + #socket; + + constructor(socket) { + this.#socket = socket; + } + + configure(config) { + this.#updateRate = config.updateRate; + } + + #startUpdate(fromTimeout) { + if (this.#updateTimeoutActive && !fromTimeout) { + return; + } + this.#updateTimeoutActive = false; + let finished = true; + const xInt = Math.trunc(this.#moveXSum); + const yInt = Math.trunc(this.#moveYSum); + if (xInt != 0 || yInt != 0) { + this.#socket.send("m" + xInt + ";" + yInt); + this.#moveXSum -= xInt; + this.#moveYSum -= yInt; + finished = false; + } + const hInt = Math.trunc(this.#scrollHSum); + const vInt = Math.trunc(this.#scrollVSum); + if (hInt != 0 || vInt != 0) { + this.#socket.send((this.#scrollFinish ? "S" : "s") + hInt + ";" + vInt); + this.#scrollHSum -= hInt; + this.#scrollVSum -= vInt; + this.#scrolling = !this.#scrollFinish; + this.#scrollFinish = false; + finished = false; + } else if (this.#scrollFinish && this.#scrolling) { + this.#socket.send("S"); + this.#scrolling = false; + this.#scrollFinish = false; + } + this.#updateTimeoutActive = !finished && this.#updateRate > 0; + if (this.#updateTimeoutActive) { + setTimeout(this.#startUpdate.bind(this), 1000 / this.#updateRate, true); + } + } + + pointerMove(deltaX, deltaY) { + this.#moveXSum += deltaX; + this.#moveYSum += deltaY; + this.#startUpdate(); + } + + pointerScroll(deltaHorizontal, deltaVertical, finish) { + this.#scrollHSum += deltaHorizontal; + this.#scrollVSum += deltaVertical; + this.#scrollFinish |= finish; + this.#startUpdate(); + }; + + pointerButton(button, press) { + this.#socket.send("b" + button + ";" + (press ? 1 : 0)); + } + + keyboardKey(key) { + this.#socket.send("k" + key); + } + + keyboardText(text) { + this.#socket.send("t" + text); + } +} diff --git a/webdata/app/keyboard.mjs b/webdata/app/keyboard.mjs new file mode 100644 index 0000000..84195d9 --- /dev/null +++ b/webdata/app/keyboard.mjs @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +import { + KEY_SUPER, KEY_BACK_SPACE, KEY_RETURN, KEY_DELETE, KEY_HOME, KEY_END, + KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, +} from "./inputcontroller.mjs"; + +export default class Keyboard { + #inputController; + #checkAllowedCallback; + + constructor(inputController, checkAllowedCallback) { + this.#inputController = inputController; + this.#checkAllowedCallback = checkAllowedCallback; + document.addEventListener("keydown", this.#handleKeydown.bind(this)); + } + + configure() {} + + #handleKeydown(event) { + if (!this.#checkAllowedCallback() || + event.ctrlKey || event.altKey || event.isComposing) { + return; + } + let key = null; + if (event.key == "OS" || event.key == "Super" || event.key == "Meta") { + key = KEY_SUPER; + } else if (event.key == "Backspace") { + key = KEY_BACK_SPACE; + } else if (event.key == "Enter") { + key = KEY_RETURN; + } else if (event.key == "Delete") { + key = KEY_DELETE; + } else if (event.key == "Home") { + key = KEY_HOME; + } else if (event.key == "End") { + key = KEY_END; + } else if (event.key == "Left" || event.key == "ArrowLeft") { + key = KEY_LEFT; + } else if (event.key == "Right" || event.key == "ArrowRight") { + key = KEY_RIGHT; + } else if (event.key == "Up" || event.key == "ArrowUp") { + key = KEY_UP; + } else if (event.key == "Down" || event.key == "ArrowDown") { + key = KEY_DOWN; + } + if (key != null) { + if (!event.shiftKey) { + event.preventDefault(); + this.#inputController.keyboardKey(key); + } + } else if (event.key.length == 1) { + event.preventDefault(); + this.#inputController.keyboardText(event.key); + } + } +} diff --git a/webdata/app/main.mjs b/webdata/app/main.mjs new file mode 100644 index 0000000..5477b89 --- /dev/null +++ b/webdata/app/main.mjs @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +import InputController, * as inputcontrollerModule from "./inputcontroller.mjs"; +import Socket from "./socket.mjs"; +import UI from "./ui.mjs"; + +const url = new URL("ws", location.href); +url.protocol = url.protocol == "http:" ? "ws:" : "wss:"; + +const socket = new Socket(url, window.location.hash.substr(1)); +const inputController = new InputController(socket); +const ui = new UI(inputController); + +socket.addEventListener("config", (event) => { + const config = event.detail; + inputController.configure(config); + ui.configure(config); +}); + +socket.addEventListener("close", () => { + ui.close(); +}); + +window.app = { + key: inputController.keyboardKey.bind(inputController), + text: inputController.keyboardText.bind(inputController), + toggleFullscreen: ui.toggleFullscreen.bind(ui), + showTextInput: ui.showTextInput.bind(ui), + showKeys: ui.showKeys.bind(ui), + setKeysPage: ui.setKeysPage.bind(ui), +}; +for (const name in inputcontrollerModule) { + if (name.startsWith("KEY_")) { + window.app[name] = inputcontrollerModule[name]; + } +} diff --git a/webdata/app/mouse.mjs b/webdata/app/mouse.mjs new file mode 100644 index 0000000..8158eb3 --- /dev/null +++ b/webdata/app/mouse.mjs @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2019, 2023 Unrud + * + * This file is part of Remote-Touchpad. + * + * Remote-Touchpad is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Remote-Touchpad is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Remote-Touchpad. If not, see . + */ + +export default class Mouse { + #moveSpeed = 1; + #scrollSpeed = 1; + + #buttons = 0; + #inputController; + + constructor (inputController, element) { + this.#inputController = inputController; + for (const type of ["touchstart", "touchend", "touchcancel", "touchmove"]) { + element.addEventListener(type, (event) => { + event.preventDefault(); + }); + } + element.addEventListener("mousedown", this.#handleMouseDownAndUp.bind(this)); + element.addEventListener("mouseup", this.#handleMouseDownAndUp.bind(this)); + element.addEventListener("mousemove", this.#handleMousemove.bind(this)); + element.addEventListener("wheel", this.#handleWheel.bind(this)); + } + + configure(config) { + this.#moveSpeed = config.mouseMoveSpeed; + this.#scrollSpeed = config.mouseScrollSpeed; + } + + #updateButtons(newButtons) { + for (let button = 0; button < 3; button += 1) { + const flag = 1 << button; + if ((newButtons&flag) != (this.#buttons & flag)) { + this.#inputController.pointerButton(button, newButtons & flag); + } + } + this.#buttons = newButtons; + } + + #handleMouseDownAndUp(event) { + this.#updateButtons(event.buttons); + } + + #handleMousemove(event) { + this.#inputController.pointerMove( + event.movementX * this.#moveSpeed, event.movementY * this.#moveSpeed); + } + + #handleWheel(event) { + if (event.deltaMode == WheelEvent.DOM_DELTA_PIXEL) { + this.#inputController.pointerScroll( + event.deltaX * this.#scrollSpeed, event.deltaY * this.#scrollSpeed, true); + } else if (event.deltaMode == WheelEvent.DOM_DELTA_LINE) { + this.#inputController.pointerScroll( + event.deltaX * 20 * this.#scrollSpeed, event.deltaY * 20 * this.#scrollSpeed, + true); + } + } +} diff --git a/webdata/app/sha256.mjs b/webdata/app/sha256.mjs new file mode 100644 index 0000000..3f7cd3f --- /dev/null +++ b/webdata/app/sha256.mjs @@ -0,0 +1,9 @@ +/** + * A JavaScript implementation of the SHA family of hashes - defined in FIPS PUB 180-4, FIPS PUB 202, + * and SP 800-185 - as well as the corresponding HMAC implementation as defined in FIPS PUB 198-1. + * + * Copyright 2008-2022 Brian Turek, 1998-2009 Paul Johnston & Contributors + * Distributed under the BSD License + * See http://caligatio.github.com/jsSHA/ for more information + */ +const t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function r(t,r,n,i){let e,s,o;const h=r||[0],u=(n=n||0)>>>3,f=-1===i?3:0;for(e=0;e>>2,h.length<=s&&h.push(0),h[s]|=t[e]<<8*(f+i*(o%4));return{value:h,binLen:8*t.length+n}}function n(n,i,e){switch(i){case"UTF8":case"UTF16BE":case"UTF16LE":break;default:throw new Error("encoding must be UTF8, UTF16BE, or UTF16LE")}switch(n){case"HEX":return function(t,r,n){return function(t,r,n,i){let e,s,o,h;if(0!=t.length%2)throw new Error("String of HEX type must be in byte increments");const u=r||[0],f=(n=n||0)>>>3,c=-1===i?3:0;for(e=0;e>>1)+f,o=h>>>2;u.length<=o;)u.push(0);u[o]|=s<<8*(c+i*(h%4))}return{value:u,binLen:4*t.length+n}}(t,r,n,e)};case"TEXT":return function(t,r,n){return function(t,r,n,i,e){let s,o,h,u,f,c,a,w,A=0;const E=n||[0],l=(i=i||0)>>>3;if("UTF8"===r)for(a=-1===e?3:0,h=0;hs?o.push(s):2048>s?(o.push(192|s>>>6),o.push(128|63&s)):55296>s||57344<=s?o.push(224|s>>>12,128|s>>>6&63,128|63&s):(h+=1,s=65536+((1023&s)<<10|1023&t.charCodeAt(h)),o.push(240|s>>>18,128|s>>>12&63,128|s>>>6&63,128|63&s)),u=0;u>>2;E.length<=f;)E.push(0);E[f]|=o[u]<<8*(a+e*(c%4)),A+=1}else for(a=-1===e?2:0,w="UTF16LE"===r&&1!==e||"UTF16LE"!==r&&1===e,h=0;h>>8),c=A+l,f=c>>>2;E.length<=f;)E.push(0);E[f]|=s<<8*(a+e*(c%4)),A+=2}return{value:E,binLen:8*A+i}}(t,i,r,n,e)};case"B64":return function(r,n,i){return function(r,n,i,e){let s,o,h,u,f,c,a,w=0;const A=n||[0],E=(i=i||0)>>>3,l=-1===e?3:0,p=r.indexOf("=");if(-1===r.search(/^[a-zA-Z0-9=+/]+$/))throw new Error("Invalid character in base-64 string");if(r=r.replace(/=/g,""),-1!==p&&p event.target.classList.remove("click"), 0); + compat.vibrate(CLICK_VIBRATION_PATTERN); + } + + #handleSendText() { + if (textInput.value) { + // normalize line endings + const text = textInput.value.replace(/\r\n?/g, "\n"); + this.#inputController.keyboardText(text); + this.#updateTextInput(""); + } else { + history.back(); + } + } + + #showScene(scene) { + this.#activeScene = scene; + if (compat.fullscreenElement() && !scene.classList.contains("allow-fullscreen")) { + compat.exitFullscreen(); + } + if (compat.pointerLockElement() && this.#activeScene != mouseScene) { + compat.exitPointerLock(); + } + textInput.value = ""; + for (const otherScene of scenes) { + otherScene.classList.toggle("hidden", otherScene != scene); + } + } + + setKeysPage(index, relative = false) { + if (relative) { + for (let i = 0; i < keysPages.length && keysPages[i].classList.contains("hidden"); + i += 1, index += 1); + } + index = ((index % keysPages.length) + keysPages.length) % keysPages.length; + sessionStorage.setItem(this.#keysActiveName, index); + for (let i = 0; i < keysPages.length; i += 1) { + keysPages[i].classList.toggle("hidden", i != index); + } + } + + showKeys(name = "", defaultPageIndex = 0) { + this.#showScene(keysScene); + this.#keysActiveName = "keys" + (name ? ":" + name : ""); + let pageIndex = parseInt(sessionStorage.getItem(this.#keysActiveName)); + if (isNaN(pageIndex)) { + pageIndex = defaultPageIndex; + } + this.setKeysPage(pageIndex); + if (history.state != this.#keysActiveName) { + history.pushState(this.#keysActiveName, ""); + } + } + + showTextInput() { + this.#showScene(textInputScene); + this.#updateTextInput(sessionStorage.getItem("text-input") || ""); + if (history.state != "text-input") { + history.pushState("text-input", ""); + } + } + + toggleFullscreen() { + if (compat.fullscreenElement()) { + compat.exitFullscreen(); + } else { + compat.requestFullscreen(document.documentElement, {navigationUI: "hide"}); + } + } + + #update() { + const fullscreenEnabled = compat.fullscreenEnabled(); + for (const element of document.querySelectorAll(".visble-if-fullscreen-enabled")) { + element.classList.toggle("hidden", !fullscreenEnabled); + } + if (!this.#ready) { + this.#showScene(this.#closed ? closedScene : openingScene); + } else if (compat.pointerLockElement()) { + this.#showScene(mouseScene); + } else if ((history.state || "").split(":")[0] == "keys") { + this.showKeys(history.state.substr("keys:".length)); + } else if (history.state == "text-input") { + this.showTextInput(); + } else { + this.#showScene(padScene); + } + } +} diff --git a/webdata/fn.js b/webdata/fn.js deleted file mode 100644 index 9a010af..0000000 --- a/webdata/fn.js +++ /dev/null @@ -1,428 +0,0 @@ -"use strict"; -/* - * Copyright (c) 2018-2019 Unrud - * - * This file is part of Remote-Touchpad. - * - * Remote-Touchpad is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Remote-Touchpad is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Remote-Touchpad. If not, see . - */ - -// [1 Touch, 2 Touches, 3 Touches] (as pixel) -const TOUCH_MOVE_THRESHOLD = [10, 15, 15]; -// Max time between consecutive touches for clicking or dragging (as milliseconds) -const TOUCH_TIMEOUT = 250; -// [[pixel/second, multiplicator], ...] -const POINTER_ACCELERATION = [ - [0, 0], - [87, 1], - [173, 1], - [553, 2] -]; - -const POINTER_BUTTON_LEFT = 0; -const POINTER_BUTTON_RIGHT = 1; -const POINTER_BUTTON_MIDDLE = 2; - -const KEY_VOLUME_MUTE = 0; -const KEY_VOLUME_DOWN = 1; -const KEY_VOLUME_UP = 2; -const KEY_MEDIA_PLAY_PAUSE = 3; -const KEY_MEDIA_PREV_TRACK = 4; -const KEY_MEDIA_NEXT_TRACK = 5; -const KEY_BROWSER_BACK = 6; -const KEY_BROWSER_FORWARD = 7; -const KEY_SUPER = 8; - -let ws; -let pad; -let padlabel; - -let touchMoved = false; -let touchStart = 0; -let touchLastEnd = 0; -let touchReleasedCount = 0; -let ongoingTouches = []; -let moveXSum = 0; -let moveYSum = 0; -let scrollXSum = 0; -let scrollYSum = 0; -let dragging = false; -let draggingTimeout = null; -let scrolling = false; - -function fullscreenEnabled() { - return (document.fullscreenEnabled || - document.webkitFullscreenEnabled || - document.mozFullScreenEnabled || - document.msFullscreenEnabled || - false); -} - -function requestFullscreen(e, options) { - if (e.requestFullscreen) { - e.requestFullscreen(options); - } else if (e.webkitRequestFullscreen) { - e.webkitRequestFullscreen(options); - } else if (e.mozRequestFullScreen) { - e.mozRequestFullScreen(options); - } else if (e.msRequestFullscreen) { - e.msRequestFullscreen(options); - } -} - -function exitFullscreen() { - if (document.exitFullscreen) { - document.exitFullscreen(); - } else if (document.webkitExitFullscreen) { - document.webkitExitFullscreen(); - } else if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else if (document.msExitFullscreen) { - document.msExitFullscreen(); - } -} - -function fullscreenElement() { - return (document.fullscreenElement || - document.webkitFullscreenElement || - document.mozFullScreenElement || - document.msFullscreenElement || - null); -} - -function addFullscreenchangeEventListener(listener) { - if (document.fullscreenElement != undefined) { - document.addEventListener("fullscreenchange", listener); - } else if (document.webkitFullscreenElement != undefined) { - document.addEventListener("webkitfullscreenchange", listener); - } else if (document.mozFullScreenElement != undefined) { - document.addEventListener("mozfullscreenchange", listener); - } else if (document.msFullscreenElement != undefined) { - document.addEventListener("MSFullscreenChange", listener); - } -} - -function copyTouch(touch, timeStamp) { - return { - identifier: touch.identifier, - pageX: touch.pageX, - pageXStart: touch.pageX, - pageY: touch.pageY, - pageYStart: touch.pageY, - timeStamp: timeStamp - }; -} - -function ongoingTouchIndexById(idToFind) { - for (let i = 0; i < ongoingTouches.length; i++) { - if (ongoingTouches[i].identifier == idToFind) { - return i; - } - } - return -1; -} - -function calculatePointerAccelerationMult(speed) { - for (let i = 0; i < POINTER_ACCELERATION.length; i++) { - let s2 = POINTER_ACCELERATION[i][0]; - let a2 = POINTER_ACCELERATION[i][1]; - if (s2 <= speed) { - continue; - } - if (i == 0) { - return a2; - } - let s1 = POINTER_ACCELERATION[i - 1][0]; - let a1 = POINTER_ACCELERATION[i - 1][1]; - return ((speed - s1) / (s2 - s1)) * (a2 - a1) + a1; - } - if (POINTER_ACCELERATION.length > 0) { - return POINTER_ACCELERATION[POINTER_ACCELERATION.length - 1][1]; - } - return 1; -} - -function onDraggingTimeout() { - draggingTimeout = null; - ws.send("b" + POINTER_BUTTON_LEFT + ";0"); -} - -function updateMoveAndScroll() { - let moveX = Math.trunc(moveXSum); - let moveY = Math.trunc(moveYSum); - if (Math.abs(moveX) >= 1 || Math.abs(moveY) >= 1) { - moveXSum -= moveX; - moveYSum -= moveY; - ws.send("m" + moveX + ";" + moveY); - } - let scrollX = Math.trunc(scrollXSum); - let scrollY = Math.trunc(scrollYSum); - if (Math.abs(scrollX) >= 1 || Math.abs(scrollY) >= 1) { - scrollXSum -= scrollX; - scrollYSum -= scrollY; - scrolling = true; - ws.send("s" + scrollX + ";" + scrollY); - } -} - -function handleStart(evt) { - // Might get called multiple times for the same touches - if (ongoingTouches.length == 0) { - touchStart = evt.timeStamp; - touchMoved = false; - } - let touches = evt.changedTouches; - for (let i = 0; i < touches.length; i++) { - if (touches[i].target != pad && touches[i].target != padlabel && - ongoingTouches.length == 0) { - continue; - } - evt.preventDefault(); - let touch = copyTouch(touches[i], evt.timeStamp); - let idx = ongoingTouchIndexById(touch.identifier); - if (idx < 0) { - ongoingTouches.push(touch); - } else { - ongoingTouches[idx] = touch; - } - touchLastEnd = 0; - if (!dragging) { - moveXSum = Math.trunc(moveXSum); - moveYSum = Math.trunc(moveYSum); - } - scrollXSum = Math.trunc(scrollXSum); - scrollYSum = Math.trunc(scrollYSum); - if (draggingTimeout != null) { - clearTimeout(draggingTimeout); - draggingTimeout = null; - dragging = true; - } - if (scrolling) { - ws.send("sf"); - scrolling = false; - } - } -} - -function handleEnd(evt) { - let touches = evt.changedTouches; - for (let i = 0; i < touches.length; i++) { - let idx = ongoingTouchIndexById(touches[i].identifier); - if (idx < 0) { - continue; - } - ongoingTouches.splice(idx, 1); - touchReleasedCount++; - touchLastEnd = evt.timeStamp; - if (scrolling) { - ws.send("sf"); - scrolling = false; - } - } - if (touchReleasedCount > TOUCH_MOVE_THRESHOLD.length) { - touchMoved = true; - } - if (ongoingTouches.length == 0 && touchReleasedCount >= 1) { - if (dragging) { - dragging = false; - ws.send("b" + POINTER_BUTTON_LEFT + ";0"); - } - if (!touchMoved && evt.timeStamp - touchStart < TOUCH_TIMEOUT) { - let button = 0; - if (touchReleasedCount == 1) { - button = POINTER_BUTTON_LEFT; - } else if (touchReleasedCount == 2) { - button = POINTER_BUTTON_RIGHT; - } else if (touchReleasedCount == 3) { - button = POINTER_BUTTON_MIDDLE; - } - ws.send("b" + button + ";1"); - if (button == POINTER_BUTTON_LEFT) { - draggingTimeout = setTimeout(onDraggingTimeout, TOUCH_TIMEOUT); - } else { - ws.send("b" + button + ";0"); - } - } - touchReleasedCount = 0; - } -} - -function handleMove(evt) { - let sumX = 0; - let sumY = 0; - let touches = evt.changedTouches; - for (let i = 0; i < touches.length; i++) { - let idx = ongoingTouchIndexById(touches[i].identifier); - if (idx < 0) { - continue; - } - if (!touchMoved) { - let dist = Math.sqrt(Math.pow(touches[i].pageX - ongoingTouches[idx].pageXStart, 2) + - Math.pow(touches[i].pageY - ongoingTouches[idx].pageYStart, 2)); - if (ongoingTouches.length > TOUCH_MOVE_THRESHOLD.length || - dist > TOUCH_MOVE_THRESHOLD[ongoingTouches.length - 1] || - evt.timeStamp - touchStart >= TOUCH_TIMEOUT) { - touchMoved = true; - } - } - let dx = touches[i].pageX - ongoingTouches[idx].pageX; - let dy = touches[i].pageY - ongoingTouches[idx].pageY; - let timeDelta = evt.timeStamp - ongoingTouches[idx].timeStamp; - sumX += dx * calculatePointerAccelerationMult(Math.abs(dx) / timeDelta * 1000); - sumY += dy * calculatePointerAccelerationMult(Math.abs(dy) / timeDelta * 1000); - ongoingTouches[idx].pageX = touches[i].pageX; - ongoingTouches[idx].pageY = touches[i].pageY; - ongoingTouches[idx].timeStamp = evt.timeStamp; - } - if (touchMoved && evt.timeStamp - touchLastEnd >= TOUCH_TIMEOUT) { - if (ongoingTouches.length == 1 || dragging) { - moveXSum += sumX; - moveYSum += sumY; - } else if (ongoingTouches.length == 2) { - scrollXSum -= sumX; - scrollYSum -= sumY; - } - updateMoveAndScroll(); - } -} - -function challengeResponse(message) { - let shaObj = new jsSHA("SHA-256", "TEXT"); - shaObj.setHMACKey(message, "TEXT"); - shaObj.update(window.location.hash.substr(1)); - return btoa(shaObj.getHMAC("BYTES")); -} - -window.addEventListener("load", function() { - let authenticated = false; - let opening = document.getElementById("opening"); - let closed = document.getElementById("closed"); - pad = document.getElementById("pad"); - padlabel = document.getElementById("padlabel"); - let keys = document.getElementById("keys"); - let keyboard = document.getElementById("keyboard"); - let fullscreenbutton = document.getElementById("fullscreenbutton"); - let text = document.getElementById("text"); - - function showScene(scene) { - [opening, closed, pad, keys, keyboard].forEach(function(e) { - e.classList.toggle("hidden", e != scene); - }); - } - - function showKeys() { - if (fullscreenElement()) { - exitFullscreen(); - } - showScene(keys); - if (history.state != "keys") { - history.pushState("keys", ""); - } - } - - function showKeyboard() { - if (fullscreenElement()) { - exitFullscreen(); - } - showScene(keyboard); - text.focus(); - if (history.state != "keyboard") { - history.pushState("keyboard", ""); - } - } - - text.value = ""; - showScene(opening); - - ws = new WebSocket( - (location.protocol == "http:" ? "ws:" : "wss:") + "//" + location.hostname + - (location.port ? ":" + location.port : "") + "/ws" - ); - - ws.onmessage = function(evt) { - if (authenticated) { - ws.close(); - return; - } - authenticated = true; - ws.send(challengeResponse(evt.data)); - if (history.state == "keyboard") { - showKeyboard(); - } else if (history.state == "keys") { - showKeys() - } else { - showScene(pad); - } - }; - - ws.onclose = function() { - if (fullscreenElement()) { - exitFullscreen(); - } - showScene(closed); - }; - - document.getElementById("keysbutton").addEventListener("click", showKeys); - document.getElementById("keyboardbutton").addEventListener("click", showKeyboard); - if (!fullscreenEnabled()) { - fullscreenbutton.classList.add("hidden"); - } - fullscreenbutton.addEventListener("click", function() { - if (fullscreenElement()) { - exitFullscreen(); - } else { - requestFullscreen(document.documentElement, {navigationUI: "hide"}); - } - }); - [{id: "browserbackbutton", key: KEY_BROWSER_BACK}, - {id: "superbutton", key: KEY_SUPER}, - {id: "browserforwardbutton", key: KEY_BROWSER_FORWARD}, - {id: "prevtrackbutton", key: KEY_MEDIA_PREV_TRACK}, - {id: "playpausebutton", key: KEY_MEDIA_PLAY_PAUSE}, - {id: "nexttrackbutton", key: KEY_MEDIA_NEXT_TRACK}, - {id: "volumedownbutton", key: KEY_VOLUME_DOWN}, - {id: "volumemutebutton", key: KEY_VOLUME_MUTE}, - {id: "volumeupbutton", key: KEY_VOLUME_UP}].forEach(function(o) { - document.getElementById(o.id).addEventListener("click", function() { - ws.send("k" + o.key); - }); - }); - document.getElementById("sendbutton").addEventListener("click", function() { - if (text.value != "") { - ws.send("t" + text.value); - text.value = ""; - } - window.history.back(); - }); - window.onpopstate = function() { - if (!pad.classList.contains("hidden") || - !keyboard.classList.contains("hidden") || - !keys.classList.contains("hidden")) { - if (history.state == "keys") { - showKeys(); - } else if (history.state == "keyboard") { - showKeyboard(); - } else { - showScene(pad); - } - } - }; - document.getElementById("reloadbutton").addEventListener("click", function() { - location.reload(); - }); - pad.addEventListener("touchstart", handleStart); - pad.addEventListener("touchend", handleEnd); - pad.addEventListener("touchcancel", handleEnd); - pad.addEventListener("touchmove", handleMove); -}); diff --git a/webdata/icon.woff b/webdata/icon.woff index 0a6cd81..84d6ccb 100644 Binary files a/webdata/icon.woff and b/webdata/icon.woff differ diff --git a/webdata/index.html b/webdata/index.html index 0ba814b..52fb46f 100644 --- a/webdata/index.html +++ b/webdata/index.html @@ -1,46 +1,100 @@ - - - - + + Remote Touchpad -
    +

    Connecting…

    -