├── .dockerignore
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── build.sh
├── coin.go
├── coin_test.go
├── install.sh
├── main.go
├── open-ledger-micro.gif
├── open-ledger-micro.service
├── transaction.go
├── transaction_test.go
├── ui
├── .angular-cli.json
├── .editorconfig
├── .gitignore
├── README.md
├── e2e
│ ├── app.e2e-spec.ts
│ ├── app.po.ts
│ └── tsconfig.e2e.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── protractor.conf.js
├── src
│ ├── app
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── dashboard
│ │ │ ├── dashboard.component.css
│ │ │ ├── dashboard.component.html
│ │ │ ├── dashboard.component.spec.ts
│ │ │ └── dashboard.component.ts
│ │ ├── generate
│ │ │ ├── generate.component.css
│ │ │ ├── generate.component.html
│ │ │ ├── generate.component.spec.ts
│ │ │ └── generate.component.ts
│ │ ├── import
│ │ │ ├── import.component.css
│ │ │ ├── import.component.html
│ │ │ ├── import.component.spec.ts
│ │ │ └── import.component.ts
│ │ ├── transaction
│ │ │ ├── transaction.component.css
│ │ │ ├── transaction.component.html
│ │ │ ├── transaction.component.spec.ts
│ │ │ └── transaction.component.ts
│ │ ├── unlock
│ │ │ ├── unlock.component.css
│ │ │ ├── unlock.component.html
│ │ │ ├── unlock.component.spec.ts
│ │ │ └── unlock.component.ts
│ │ ├── wallet.service.spec.ts
│ │ └── wallet.service.ts
│ ├── assets
│ │ ├── .gitkeep
│ │ ├── css
│ │ │ ├── bootstrap-select.min.css
│ │ │ ├── bootstrap-theme.css.map
│ │ │ ├── bootstrap-theme.min.css
│ │ │ ├── bootstrap-theme.min.css.map
│ │ │ ├── bootstrap.css.map
│ │ │ ├── bootstrap.min.css
│ │ │ ├── bootstrap.min.css.map
│ │ │ ├── fa-brands.min.css
│ │ │ ├── fa-regular.min.css
│ │ │ ├── fa-solid.min.css
│ │ │ ├── fontawesome-all.min.css
│ │ │ └── fontawesome.min.css
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ ├── glyphicons-halflings-regular.woff
│ │ │ └── glyphicons-halflings-regular.woff2
│ │ ├── js
│ │ │ ├── bootstrap-select.min.js
│ │ │ ├── bootstrap.min.js
│ │ │ └── jquery-3.2.1.min.js
│ │ └── webfonts
│ │ │ ├── fa-brands-400.eot
│ │ │ ├── fa-brands-400.svg
│ │ │ ├── fa-brands-400.ttf
│ │ │ ├── fa-brands-400.woff
│ │ │ ├── fa-brands-400.woff2
│ │ │ ├── fa-regular-400.eot
│ │ │ ├── fa-regular-400.svg
│ │ │ ├── fa-regular-400.ttf
│ │ │ ├── fa-regular-400.woff
│ │ │ ├── fa-regular-400.woff2
│ │ │ ├── fa-solid-900.eot
│ │ │ ├── fa-solid-900.svg
│ │ │ ├── fa-solid-900.ttf
│ │ │ ├── fa-solid-900.woff
│ │ │ └── fa-solid-900.woff2
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.css
│ ├── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── typings.d.ts
├── tsconfig.json
└── tslint.json
├── wallet.go
└── wallet_test.go
/.dockerignore:
--------------------------------------------------------------------------------
1 | rice-box.go
2 | open-ledger-micro
3 | open-ledger
4 | node_modules
5 | dist
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | open-ledger-micro
2 | *.dat
3 | rice-box.go
4 |
5 | .DS_Store*
6 | Thumbs.db*
7 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:latest
2 |
3 | RUN apt-get update
4 | RUN apt-get install wget git --yes --quiet
5 |
6 | RUN wget -O go.tgz "https://dl.google.com/go/go1.10.linux-amd64.tar.gz"
7 | RUN wget -O nodejs.tgz "https://nodejs.org/dist/v9.9.0/node-v9.9.0-linux-x64.tar.gz"
8 | RUN tar -C /usr/local/ -xzf go.tgz
9 | RUN tar -C /usr/local/ -xzf nodejs.tgz && mv /usr/local/node-v9.9.0-linux-x64 /usr/local/nodejs
10 | RUN rm go.tgz
11 | RUN rm nodejs.tgz
12 |
13 | EXPOSE 80 443 12345
14 |
15 | ENV GOPATH /go
16 | ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
17 | ENV PATH /usr/local/nodejs/bin:$PATH
18 |
19 | RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
20 | RUN mkdir -p "$GOPATH/src/github.com/nraboy/open-ledger-micro"
21 |
22 | WORKDIR $GOPATH
23 |
24 | COPY . $GOPATH/src/github.com/nraboy/open-ledger-micro/
25 |
26 | RUN go get github.com/GeertJohan/go.rice
27 | RUN go get github.com/GeertJohan/go.rice/rice
28 | RUN go get github.com/gorilla/mux
29 | RUN go get github.com/btcsuite/btcd
30 | RUN go get github.com/btcsuite/btcutil
31 | RUN go get github.com/gorilla/handlers
32 |
33 | RUN cd $GOPATH/src/github.com/nraboy/open-ledger-micro/ui && npm install && npm run build
34 | RUN cd $GOPATH/src/github.com/nraboy/open-ledger-micro && rice embed-go && go build && go install
35 |
36 | CMD ["open-ledger-micro"]
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 The Polyglot Developer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Open Ledger Micro
2 |
3 | An open source hardware wallet application for Raspberry Pi Zero devices, written in Golang and Angular.
4 |
5 | 
6 |
7 | ## How it Works
8 |
9 | The Raspberry Pi Zero is a $5.00 computer with no WiFi or Bluetooth and can be configured to emulate Ethernet over USB. The application written in Go and Angular, serves a RESTful API to be consumed with the integrated Angular application.
10 |
11 | Once configured, software like Bonjour on a host machine will allow the Raspberry Pi Zero to be accessed by its hostname. For example, http://raspberrypi.local would show the Angular web application.
12 |
13 | Sensitive information such as private keys are encrypted on the Raspberry Pi and are never exposed through HTTP. Transactions are created and signed directly on the device and returned to the Angular application.
14 |
15 | ## Test with Docker
16 |
17 | For testing purposes, a Docker blueprint has been provided with this repository. Assuming you've got Docker installed, execute the following:
18 |
19 | ```
20 | git clone https://github.com/nraboy/open-ledger-micro
21 | cd open-ledger-micro
22 | docker build -t open-ledger-micro .
23 | docker run -d -p 12345:12345 --name open-ledger-micro open-ledger-micro
24 | ```
25 |
26 | The above command will build a Docker image from the repository which includes building the Angular project and bundling it in a built Golang binary. When a container is deployed, the compiled binary will be ran.
27 |
28 | ## Disclaimer
29 |
30 | I, Nic Raboy, am not a cryptocurrency or cryptography expert. Take time to understand how Bitcoin and other cryptocurrencies work and use this project at your own risk. If you lose your keys or send your coins into a black hole, nobody is responsible except yourself.
31 |
32 | ## Contact Me
33 |
34 | If you'd like to contact me about the project, find me on Twitter at [@nraboy](https://www.twitter.com/nraboy).
35 |
36 | ## Donate
37 |
38 | If you found this project useful, consider donating so more resources can be invested in it or other quality projects like it:
39 |
40 | PayPal - [https://paypal.me/nraboy](https://paypal.me/nraboy)
41 |
42 | Bitcoin - [15sf3hLC4JqdBzBxSZ6PWFhbVy85Wo9Whd](bitcoin:15sf3hLC4JqdBzBxSZ6PWFhbVy85Wo9Whd)
43 |
44 | ## Resources
45 |
46 | [Create a Bitcoin Hardware Wallet with Golang and a Raspberry Pi Zero](https://www.thepolyglotdeveloper.com/2018/03/create-bitcoin-hardware-wallet-golang-raspberry-pi-zero)
47 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | cd ui
2 | npm install
3 | npm run build
4 | cd ..
5 | export PATH=$PATH:$GOPATH/bin
6 | rice embed-go
7 | go build
--------------------------------------------------------------------------------
/coin.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/btcsuite/btcd/btcec"
7 | "github.com/btcsuite/btcd/chaincfg"
8 | "github.com/btcsuite/btcd/wire"
9 | "github.com/btcsuite/btcutil"
10 | )
11 |
12 | type Coin struct {
13 | Name string `json:"name"`
14 | Symbol string `json:"symbol"`
15 | WIF string `json:"wif,omitempty"`
16 | UncompressedAddress string `json:"uncompressed_address"`
17 | CompressedAddress string `json:"compressed_address"`
18 | }
19 |
20 | type Network struct {
21 | name string
22 | symbol string
23 | xpubkey byte
24 | xprivatekey byte
25 | magic wire.BitcoinNet
26 | }
27 |
28 | var network = map[string]Network{
29 | "rdd": {name: "reddcoin", symbol: "rdd", xpubkey: 0x3d, xprivatekey: 0xbd, magic: 0xfbc0b6db},
30 | "dgb": {name: "digibyte", symbol: "dgb", xpubkey: 0x1e, xprivatekey: 0x80, magic: 0xfac3b6da},
31 | "btc": {name: "bitcoin", symbol: "btc", xpubkey: 0x00, xprivatekey: 0x80, magic: 0xf9beb4d9},
32 | "ltc": {name: "litecoin", symbol: "ltc", xpubkey: 0x30, xprivatekey: 0xb0, magic: 0xfbc0b6db},
33 | "dash": {name: "dash", symbol: "dash", xpubkey: 0x4c, xprivatekey: 0xcc, magic: 0xd9b4bef9},
34 | }
35 |
36 | func (network Network) GetNetworkParams() *chaincfg.Params {
37 | networkParams := &chaincfg.MainNetParams
38 | networkParams.Name = network.name
39 | networkParams.Net = network.magic
40 | networkParams.PubKeyHashAddrID = network.xpubkey
41 | networkParams.PrivateKeyID = network.xprivatekey
42 | return networkParams
43 | }
44 |
45 | func (coin *Coin) Generate(network Network) error {
46 | if network == (Network{}) {
47 | return errors.New("Unsupported cryptocurrency symbol provided")
48 | }
49 | secret, err := btcec.NewPrivateKey(btcec.S256())
50 | if err != nil {
51 | return err
52 | }
53 | wif, err := btcutil.NewWIF(secret, network.GetNetworkParams(), false)
54 | if err != nil {
55 | return err
56 | }
57 | coin.WIF = wif.String()
58 | uncompressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.GetNetworkParams())
59 | if err != nil {
60 | return err
61 | }
62 | compressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeCompressed(), network.GetNetworkParams())
63 | if err != nil {
64 | return err
65 | }
66 | coin.UncompressedAddress = uncompressedAddress.EncodeAddress()
67 | coin.CompressedAddress = compressedAddress.EncodeAddress()
68 | coin.Name = network.name
69 | coin.Symbol = network.symbol
70 | return nil
71 | }
72 |
73 | func (coin *Coin) Import(network Network) error {
74 | if network == (Network{}) {
75 | return errors.New("Unsupported cryptocurrency symbol provided")
76 | }
77 | wif, err := btcutil.DecodeWIF(coin.WIF)
78 | if err != nil {
79 | return err
80 | }
81 | if !wif.IsForNet(network.GetNetworkParams()) {
82 | return errors.New("The WIF string is not valid for the `" + network.name + "` network")
83 | }
84 | uncompressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.GetNetworkParams())
85 | if err != nil {
86 | return err
87 | }
88 | compressedAddress, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeCompressed(), network.GetNetworkParams())
89 | if err != nil {
90 | return err
91 | }
92 | coin.WIF = wif.String()
93 | coin.UncompressedAddress = uncompressedAddress.EncodeAddress()
94 | coin.CompressedAddress = compressedAddress.EncodeAddress()
95 | coin.Name = network.name
96 | coin.Symbol = network.symbol
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/coin_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestCoinGenerate(t *testing.T) {
10 | var coin Coin
11 | err := coin.Generate(network["btc"])
12 | assert.Nil(t, err, "The `err` should not be `nil`")
13 | err = coin.Generate(network["test"])
14 | assert.NotNil(t, err, "The `err` should be `nil`")
15 | }
16 |
17 | func TestCoinImport(t *testing.T) {
18 | coin1 := Coin{Symbol: "btc", WIF: "1234"}
19 | err := coin1.Import(network[coin1.Symbol])
20 | assert.NotNil(t, err.Error(), "The `err` should not be `nil`")
21 | coin2 := Coin{Symbol: "test", WIF: "1234"}
22 | err = coin2.Import(network[coin2.Symbol])
23 | assert.NotNil(t, err.Error(), "The `err` should not be `nil`")
24 | coin3 := Coin{Symbol: "btc", WIF: "6vNUWjwJewkaC8TYhNSVa6nZg86x5eQUAgkoPs84YXBFVHemNSk"}
25 | err = coin3.Import(network[coin3.Symbol])
26 | assert.NotNil(t, err.Error(), "The `err` should not be `nil`")
27 | coin4 := Coin{Symbol: "ltc", WIF: "6vNUWjwJewkaC8TYhNSVa6nZg86x5eQUAgkoPs84YXBFVHemNSk"}
28 | err = coin4.Import(network[coin4.Symbol])
29 | assert.Nil(t, err, "The `err` should not be `nil`")
30 | }
31 |
32 | func TestCoinValidity(t *testing.T) {
33 | var coin1 Coin
34 | coin1.Generate(network["ltc"])
35 | coin2 := Coin{Symbol: "ltc", WIF: coin1.WIF}
36 | coin2.Import(network[coin2.Symbol])
37 | assert.Equal(t, coin1, coin2, "The two coins should match")
38 | }
39 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | cd ui
2 | npm install
3 | npm run build
4 | cd ..
5 | export PATH=$PATH:$GOPATH/bin
6 | rice embed-go
7 | env GOOS=linux GOARCH=arm GOARM=5 go build
8 | scp open-ledger-micro coin.local:~/
9 | rm open-ledger
10 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "log"
8 | "net/http"
9 | "strconv"
10 | "strings"
11 | "time"
12 |
13 | rice "github.com/GeertJohan/go.rice"
14 | "github.com/gorilla/handlers"
15 | "github.com/gorilla/mux"
16 | )
17 |
18 | func LogRequest(request *http.Request) {
19 | fmt.Printf("%s %s %q\n", time.Now(), request.Method, request.URL.Path)
20 | }
21 |
22 | func CreateWalletEndpoint(response http.ResponseWriter, request *http.Request) {
23 | LogRequest(request)
24 | response.Header().Set("Content-Type", "application/json")
25 | queryParams := request.URL.Query()
26 | var wallet Wallet
27 | json.NewDecoder(request.Body).Decode(&wallet)
28 | if queryParams.Get("key") == "" {
29 | response.WriteHeader(500)
30 | response.Write([]byte(`{ "message": "A password is required to create a wallet" }`))
31 | return
32 | }
33 | wallet.Create(queryParams.Get("key"))
34 | json.NewEncoder(response).Encode(wallet)
35 | }
36 |
37 | func DestroyWalletEndpoint(response http.ResponseWriter, request *http.Request) {
38 | LogRequest(request)
39 | response.Header().Set("Content-Type", "application/json")
40 | var wallet Wallet
41 | err := wallet.Destroy()
42 | if err != nil {
43 | response.WriteHeader(500)
44 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
45 | return
46 | }
47 | json.NewEncoder(response).Encode(wallet)
48 | }
49 |
50 | func DumpWalletEndpoint(response http.ResponseWriter, request *http.Request) {
51 | LogRequest(request)
52 | response.Header().Set("Content-Type", "application/json")
53 | queryParams := request.URL.Query()
54 | if queryParams.Get("key") == "" {
55 | response.WriteHeader(500)
56 | response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
57 | return
58 | }
59 | var wallet Wallet
60 | err := wallet.Dump(queryParams.Get("key"))
61 | if err != nil {
62 | response.WriteHeader(500)
63 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
64 | return
65 | }
66 | json.NewEncoder(response).Encode(wallet)
67 | }
68 |
69 | func ImportCoinEndpoint(response http.ResponseWriter, request *http.Request) {
70 | LogRequest(request)
71 | response.Header().Set("Content-Type", "application/json")
72 | queryParams := request.URL.Query()
73 | var coin Coin
74 | json.NewDecoder(request.Body).Decode(&coin)
75 | err := coin.Import(network[strings.ToLower(coin.Symbol)])
76 | if err != nil {
77 | response.WriteHeader(500)
78 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
79 | return
80 | }
81 | var wallet Wallet
82 | err = wallet.DecryptFile(queryParams.Get("key"))
83 | if err != nil {
84 | response.WriteHeader(500)
85 | response.Write([]byte(`{ "message": "The password is not correct" }`))
86 | return
87 | }
88 | err = wallet.Import(coin, queryParams.Get("key"))
89 | if err != nil {
90 | response.WriteHeader(500)
91 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
92 | return
93 | }
94 | json.NewEncoder(response).Encode(coin)
95 | }
96 |
97 | func CreateCoinEndpoint(response http.ResponseWriter, request *http.Request) {
98 | LogRequest(request)
99 | response.Header().Set("Content-Type", "application/json")
100 | var coin Coin
101 | json.NewDecoder(request.Body).Decode(&coin)
102 | queryParams := request.URL.Query()
103 | if queryParams.Get("key") == "" {
104 | response.WriteHeader(500)
105 | response.Write([]byte(`{ "message": "A password is required to use a wallet" }`))
106 | return
107 | }
108 | coin.Generate(network[strings.ToLower(coin.Symbol)])
109 | var wallet Wallet
110 | err := wallet.DecryptFile(queryParams.Get("key"))
111 | if err != nil {
112 | response.WriteHeader(500)
113 | response.Write([]byte(`{ "message": "The password is not correct" }`))
114 | return
115 | }
116 | err = wallet.Import(coin, queryParams.Get("key"))
117 | if err != nil {
118 | response.WriteHeader(500)
119 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
120 | return
121 | }
122 | json.NewEncoder(response).Encode(coin)
123 | }
124 |
125 | func AuthenticateWalletEndpoint(response http.ResponseWriter, request *http.Request) {
126 | LogRequest(request)
127 | response.Header().Set("Content-Type", "application/json")
128 | queryParams := request.URL.Query()
129 | if queryParams.Get("key") == "" {
130 | response.WriteHeader(500)
131 | response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
132 | return
133 | }
134 | var wallet Wallet
135 | authenticated := wallet.Authenticate(queryParams.Get("key"))
136 | response.Write([]byte(`{ "authenticated": ` + strconv.FormatBool(authenticated) + ` }`))
137 | }
138 |
139 | func GetAddressesEndpoint(response http.ResponseWriter, request *http.Request) {
140 | LogRequest(request)
141 | response.Header().Set("Content-Type", "application/json")
142 | queryParams := request.URL.Query()
143 | if queryParams.Get("key") == "" {
144 | response.WriteHeader(500)
145 | response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
146 | return
147 | }
148 | var wallet Wallet
149 | err := wallet.GetAddresses(queryParams.Get("key"))
150 | if err != nil {
151 | response.WriteHeader(500)
152 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
153 | return
154 | }
155 | json.NewEncoder(response).Encode(wallet)
156 | }
157 |
158 | func ExportWalletEndpoint(response http.ResponseWriter, request *http.Request) {
159 | LogRequest(request)
160 | response.Header().Set("Content-Disposition", "attachment; filename=wallet.dat")
161 | file, err := ioutil.ReadFile("wallet.dat")
162 | if err != nil {
163 | response.WriteHeader(500)
164 | response.Write([]byte(err.Error()))
165 | return
166 | }
167 | response.Write(file)
168 | }
169 |
170 | func CreateTransactionEndpoint(response http.ResponseWriter, request *http.Request) {
171 | LogRequest(request)
172 | response.Header().Set("Content-Type", "application/json")
173 | var transaction Transaction
174 | json.NewDecoder(request.Body).Decode(&transaction)
175 | queryParams := request.URL.Query()
176 | if queryParams.Get("key") == "" {
177 | response.WriteHeader(500)
178 | response.Write([]byte(`{ "message": "A password is required to decrypt a wallet" }`))
179 | return
180 | }
181 | var wallet Wallet
182 | err := wallet.Dump(queryParams.Get("key"))
183 | if err != nil {
184 | response.WriteHeader(500)
185 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
186 | return
187 | }
188 | for _, coin := range wallet.Coins {
189 | if coin.UncompressedAddress == transaction.SourceAddress || coin.CompressedAddress == transaction.SourceAddress {
190 | tx, err := CreateTransaction(network[strings.ToLower(coin.Symbol)], coin.WIF, transaction.DestinationAddress, transaction.Amount, transaction.TxId)
191 | if err != nil {
192 | response.WriteHeader(500)
193 | response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
194 | return
195 | }
196 | json.NewEncoder(response).Encode(tx)
197 | return
198 | }
199 | }
200 | response.WriteHeader(500)
201 | response.Write([]byte(`{ "message": "The source address does not exist in the wallet" }`))
202 | }
203 |
204 | func main() {
205 | fmt.Println("Starting the application...")
206 | router := mux.NewRouter()
207 | api := router.PathPrefix("/api").Subrouter()
208 | api.HandleFunc("/wallet", CreateWalletEndpoint).Methods("POST")
209 | api.HandleFunc("/wallet", DestroyWalletEndpoint).Methods("DELETE")
210 | api.HandleFunc("/wallet", DumpWalletEndpoint).Methods("GET")
211 | api.HandleFunc("/addresses", GetAddressesEndpoint).Methods("GET")
212 | api.HandleFunc("/authenticate", AuthenticateWalletEndpoint).Methods("POST")
213 | api.HandleFunc("/import-coin", ImportCoinEndpoint).Methods("POST")
214 | api.HandleFunc("/create-coin", CreateCoinEndpoint).Methods("POST")
215 | api.HandleFunc("/backup", ExportWalletEndpoint).Methods("GET")
216 | api.HandleFunc("/transaction", CreateTransactionEndpoint).Methods("POST")
217 | router.PathPrefix("/").Handler(http.FileServer(rice.MustFindBox("ui/dist").HTTPBox()))
218 | fmt.Println("Listening at :12345...")
219 | log.Fatal(http.ListenAndServe(":12345", handlers.CORS(handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}), handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"}), handlers.AllowedOrigins([]string{"*"}))(router)))
220 | }
221 |
--------------------------------------------------------------------------------
/open-ledger-micro.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/open-ledger-micro.gif
--------------------------------------------------------------------------------
/open-ledger-micro.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Open Ledger Micro for Cryptocurrency
3 |
4 | [Service]
5 | Type=simple
6 | ExecStart=/usr/local/bin/open-ledger
7 | Restart=always
8 |
9 | [Install]
10 | WantedBy=multi-user.target
11 |
--------------------------------------------------------------------------------
/transaction.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/hex"
6 |
7 | "github.com/btcsuite/btcd/chaincfg/chainhash"
8 | "github.com/btcsuite/btcd/txscript"
9 | "github.com/btcsuite/btcd/wire"
10 | "github.com/btcsuite/btcutil"
11 | )
12 |
13 | type Transaction struct {
14 | TxId string `json:"txid"`
15 | SourceAddress string `json:"source_address"`
16 | DestinationAddress string `json:"destination_address"`
17 | Amount int64 `json:"amount"`
18 | UnsignedTx string `json:"unsignedtx"`
19 | SignedTx string `json:"signedtx"`
20 | }
21 |
22 | func CreateTransaction(network Network, secret string, destination string, amount int64, txHash string) (Transaction, error) {
23 | var transaction Transaction
24 | wif, err := btcutil.DecodeWIF(secret)
25 | if err != nil {
26 | return Transaction{}, err
27 | }
28 | addresspubkey, err := btcutil.NewAddressPubKey(wif.PrivKey.PubKey().SerializeUncompressed(), network.GetNetworkParams())
29 | if err != nil {
30 | return Transaction{}, err
31 | }
32 | sourceTx := wire.NewMsgTx(wire.TxVersion)
33 | sourceUtxoHash, err := chainhash.NewHashFromStr(txHash)
34 | if err != nil {
35 | return Transaction{}, err
36 | }
37 | sourceUtxo := wire.NewOutPoint(sourceUtxoHash, 0)
38 | sourceTxIn := wire.NewTxIn(sourceUtxo, nil, nil)
39 | destinationAddress, err := btcutil.DecodeAddress(destination, network.GetNetworkParams())
40 | sourceAddress, err := btcutil.DecodeAddress(addresspubkey.EncodeAddress(), network.GetNetworkParams())
41 | if err != nil {
42 | return Transaction{}, err
43 | }
44 | destinationPkScript, err := txscript.PayToAddrScript(destinationAddress)
45 | if err != nil {
46 | return Transaction{}, err
47 | }
48 | sourcePkScript, err := txscript.PayToAddrScript(sourceAddress)
49 | if err != nil {
50 | return Transaction{}, err
51 | }
52 | sourceTxOut := wire.NewTxOut(amount, sourcePkScript)
53 | sourceTx.AddTxIn(sourceTxIn)
54 | sourceTx.AddTxOut(sourceTxOut)
55 | sourceTxHash := sourceTx.TxHash()
56 | redeemTx := wire.NewMsgTx(wire.TxVersion)
57 | destinationUtxo := wire.NewOutPoint(&sourceTxHash, 0)
58 | redeemTxIn := wire.NewTxIn(destinationUtxo, nil, nil)
59 | redeemTx.AddTxIn(redeemTxIn)
60 | redeemTxOut := wire.NewTxOut(amount, destinationPkScript)
61 | redeemTx.AddTxOut(redeemTxOut)
62 | sigScript, err := txscript.SignatureScript(redeemTx, 0, sourceTx.TxOut[0].PkScript, txscript.SigHashAll, wif.PrivKey, false)
63 | if err != nil {
64 | return Transaction{}, err
65 | }
66 | redeemTx.TxIn[0].SignatureScript = sigScript
67 | flags := txscript.StandardVerifyFlags
68 | vm, err := txscript.NewEngine(sourceTx.TxOut[0].PkScript, redeemTx, 0, flags, nil, nil, amount)
69 | if err != nil {
70 | return Transaction{}, err
71 | }
72 | if err := vm.Execute(); err != nil {
73 | return Transaction{}, err
74 | }
75 | var unsignedTx bytes.Buffer
76 | var signedTx bytes.Buffer
77 | sourceTx.Serialize(&unsignedTx)
78 | redeemTx.Serialize(&signedTx)
79 | transaction.TxId = sourceTxHash.String()
80 | transaction.UnsignedTx = hex.EncodeToString(unsignedTx.Bytes())
81 | transaction.Amount = amount
82 | transaction.SignedTx = hex.EncodeToString(signedTx.Bytes())
83 | transaction.SourceAddress = sourceAddress.EncodeAddress()
84 | transaction.DestinationAddress = destinationAddress.EncodeAddress()
85 | return transaction, nil
86 | }
87 |
--------------------------------------------------------------------------------
/transaction_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestCreateTransaction(t *testing.T) {
10 | transaction, err := CreateTransaction(network["btc"], "5HusYj2b2x4nroApgfvaSfKYZhRbKFH41bVyPooymbC6KfgSXdD", "1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa", 91234, "81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48")
11 | assert.Nil(t, err, "The `err` should be `nil`")
12 | assert.Equal(t, transaction.SourceAddress, "1MMMMSUb1piy2ufrSguNUdFmAcvqrQF8M5", "The `SourceAddress` does not match")
13 | assert.Equal(t, transaction.DestinationAddress, "1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa", "The `DestinationAddress` does not match")
14 | assert.Equal(t, transaction.TxId, "4e8378675bcf6a389c8cfe246094aafa44249e48ab88a40e6fda3bf0f44f916a", "The `TxId` does not match")
15 | assert.Equal(t, transaction.UnsignedTx, "0100000001484d40d45b9ea0d652fca8258ab7caa42541eb52975857f96fb50cd732c8b4810000000000ffffffff0162640100000000001976a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac00000000", "The `UnsignedTx` does not match")
16 | assert.Equal(t, transaction.SignedTx, "01000000016a914ff4f03bda6f0ea488ab489e2444faaa946024fe8c9c386acf5b6778834e000000008b483045022100904dbeddeecccf6391ac92922381ae006bf244c002f42e195daa0a9837a4b5820220461677f9dbb7d9580e268ac486cfeb4b9d87bfdd6d4e7b1be09b8e6f5cc0a70701410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcdffffffff0162640100000000001976a914c8e90996c7c6080ee06284600c684ed904d14c5c88ac00000000", "The `SignedTx` does not match")
17 | assert.Equal(t, transaction.Amount, int64(91234), "The `Amount` does not match")
18 | transaction = Transaction{}
19 | transaction, err = CreateTransaction(network["btc"], "6KBco75idUx4umGRdrSmZnNKPgW4UuGMNFnvLL6JqaBVA7ka7Y", "1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa", 91234, "81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48")
20 | assert.NotNil(t, err, "The `err` should not be `nil`")
21 | }
22 |
--------------------------------------------------------------------------------
/ui/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "ui"
5 | },
6 | "apps": [
7 | {
8 | "root": "src",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "app",
21 | "styles": [
22 | "styles.css"
23 | ],
24 | "scripts": [],
25 | "environmentSource": "environments/environment.ts",
26 | "environments": {
27 | "dev": "environments/environment.ts",
28 | "prod": "environments/environment.prod.ts"
29 | }
30 | }
31 | ],
32 | "e2e": {
33 | "protractor": {
34 | "config": "./protractor.conf.js"
35 | }
36 | },
37 | "lint": [
38 | {
39 | "project": "src/tsconfig.app.json",
40 | "exclude": "**/node_modules/**"
41 | },
42 | {
43 | "project": "src/tsconfig.spec.json",
44 | "exclude": "**/node_modules/**"
45 | },
46 | {
47 | "project": "e2e/tsconfig.e2e.json",
48 | "exclude": "**/node_modules/**"
49 | }
50 | ],
51 | "test": {
52 | "karma": {
53 | "config": "./karma.conf.js"
54 | }
55 | },
56 | "defaults": {
57 | "styleExt": "css",
58 | "component": {}
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ui/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/ui/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /dist-server
6 | /tmp
7 | /out-tsc
8 |
9 | # dependencies
10 | /node_modules
11 |
12 | # IDEs and editors
13 | /.idea
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 |
21 | # IDE - VSCode
22 | .vscode/*
23 | !.vscode/settings.json
24 | !.vscode/tasks.json
25 | !.vscode/launch.json
26 | !.vscode/extensions.json
27 |
28 | # misc
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | yarn-error.log
35 | testem.log
36 | /typings
37 |
38 | # e2e
39 | /e2e/*.js
40 | /e2e/*.map
41 |
42 | # System Files
43 | .DS_Store
44 | Thumbs.db
45 |
--------------------------------------------------------------------------------
/ui/README.md:
--------------------------------------------------------------------------------
1 | # Ui
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.3.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/ui/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('ui App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getParagraphText()).toEqual('Welcome to app!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/ui/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ui/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "target": "es5",
8 | "types": [
9 | "jasmine",
10 | "jasminewd2",
11 | "node"
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ui/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular/cli'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular/cli/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | reports: [ 'html', 'lcovonly' ],
20 | fixWebpackSourcePaths: true
21 | },
22 | angularCli: {
23 | environment: 'dev'
24 | },
25 | reporters: ['progress', 'kjhtml'],
26 | port: 9876,
27 | colors: true,
28 | logLevel: config.LOG_INFO,
29 | autoWatch: true,
30 | browsers: ['Chrome'],
31 | singleRun: false
32 | });
33 | };
34 |
--------------------------------------------------------------------------------
/ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ui",
3 | "version": "0.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "ng": "ng",
7 | "start": "ng serve",
8 | "build": "ng build --prod",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "e2e": "ng e2e"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/animations": "5.2.8",
16 | "@angular/common": "5.2.8",
17 | "@angular/compiler": "5.2.8",
18 | "@angular/core": "5.2.8",
19 | "@angular/forms": "5.2.8",
20 | "@angular/http": "5.2.8",
21 | "@angular/platform-browser": "5.2.8",
22 | "@angular/platform-browser-dynamic": "5.2.8",
23 | "@angular/router": "5.2.8",
24 | "core-js": "^2.4.1",
25 | "file-saver": "^1.3.3",
26 | "rxjs": "^5.5.6",
27 | "zone.js": "^0.8.20"
28 | },
29 | "devDependencies": {
30 | "@angular/cli": "1.7.3",
31 | "@angular/compiler-cli": "5.2.8",
32 | "@angular/language-service": "5.2.8",
33 | "@types/jasmine": "~2.8.3",
34 | "@types/jasminewd2": "~2.0.2",
35 | "@types/node": "~6.0.60",
36 | "codelyzer": "^4.0.1",
37 | "jasmine-core": "~2.8.0",
38 | "jasmine-spec-reporter": "~4.2.1",
39 | "karma": "~2.0.0",
40 | "karma-chrome-launcher": "~2.2.0",
41 | "karma-coverage-istanbul-reporter": "^1.2.1",
42 | "karma-jasmine": "~1.1.0",
43 | "karma-jasmine-html-reporter": "^0.2.2",
44 | "protractor": "~5.1.2",
45 | "ts-node": "~4.1.0",
46 | "tslint": "~5.9.1",
47 | "typescript": "2.6.2"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ui/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:4200/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | require('ts-node').register({
24 | project: 'e2e/tsconfig.e2e.json'
25 | });
26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/ui/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/app/app.component.css
--------------------------------------------------------------------------------
/ui/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
27 |
--------------------------------------------------------------------------------
/ui/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 | describe('AppComponent', () => {
4 | beforeEach(async(() => {
5 | TestBed.configureTestingModule({
6 | declarations: [
7 | AppComponent
8 | ],
9 | }).compileComponents();
10 | }));
11 | it('should create the app', async(() => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.debugElement.componentInstance;
14 | expect(app).toBeTruthy();
15 | }));
16 | it(`should have as title 'app'`, async(() => {
17 | const fixture = TestBed.createComponent(AppComponent);
18 | const app = fixture.debugElement.componentInstance;
19 | expect(app.title).toEqual('app');
20 | }));
21 | it('should render title in a h1 tag', async(() => {
22 | const fixture = TestBed.createComponent(AppComponent);
23 | fixture.detectChanges();
24 | const compiled = fixture.debugElement.nativeElement;
25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
26 | }));
27 | });
28 |
--------------------------------------------------------------------------------
/ui/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router } from "@angular/router";
3 | import { saveAs } from "file-saver/FileSaver";
4 | import { WalletService } from "./wallet.service";
5 |
6 | @Component({
7 | selector: 'app-root',
8 | templateUrl: './app.component.html',
9 | styleUrls: ['./app.component.css']
10 | })
11 | export class AppComponent implements OnInit {
12 |
13 | public authenticated: boolean;
14 |
15 | public constructor(private router: Router, private wallet: WalletService) {
16 | this.authenticated = false;
17 | }
18 |
19 | public ngOnInit() {
20 | if(!this.authenticated) {
21 | this.router.navigate(["/unlock"], { replaceUrl: true });
22 | }
23 | this.wallet.isAuthenticated().subscribe(authenticated => {
24 | this.authenticated = authenticated;
25 | if(!authenticated) {
26 | this.router.navigate(["/unlock"], { replaceUrl: true });
27 | }
28 | });
29 | }
30 |
31 | public backup() {
32 | this.wallet.backup()
33 | .subscribe(result => {
34 | var blob = new Blob([result], { type: "text/plain" });
35 | saveAs(blob, "wallet.dat")
36 | })
37 | }
38 |
39 | public disconnect() {
40 | this.wallet.disconnect();
41 | this.router.navigate(["/unlock"]);
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/ui/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { RouterModule } from "@angular/router";
4 | import { FormsModule } from "@angular/forms";
5 | import { HttpModule } from "@angular/http";
6 |
7 | import { AppComponent } from './app.component';
8 | import { UnlockComponent } from './unlock/unlock.component';
9 | import { DashboardComponent } from './dashboard/dashboard.component';
10 | import { GenerateComponent } from './generate/generate.component';
11 | import { WalletService } from "./wallet.service";
12 | import { ImportComponent } from './import/import.component';
13 | import { TransactionComponent } from './transaction/transaction.component';
14 |
15 | const routes = [
16 | { path: "", redirectTo: "/unlock", pathMatch: "full" },
17 | { path: "unlock", component: UnlockComponent },
18 | { path: "generate", component: GenerateComponent },
19 | { path: "dashboard", component: DashboardComponent },
20 | { path: "import", component: ImportComponent },
21 | { path: "transaction/:source", component: TransactionComponent },
22 | ];
23 |
24 | @NgModule({
25 | declarations: [
26 | AppComponent,
27 | UnlockComponent,
28 | DashboardComponent,
29 | GenerateComponent,
30 | ImportComponent,
31 | TransactionComponent
32 | ],
33 | imports: [
34 | BrowserModule,
35 | FormsModule,
36 | HttpModule,
37 | RouterModule,
38 | RouterModule.forRoot(routes, { useHash: true })
39 | ],
40 | providers: [WalletService],
41 | bootstrap: [AppComponent]
42 | })
43 | export class AppModule { }
44 |
--------------------------------------------------------------------------------
/ui/src/app/dashboard/dashboard.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/app/dashboard/dashboard.component.css
--------------------------------------------------------------------------------
/ui/src/app/dashboard/dashboard.component.html:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/ui/src/app/dashboard/dashboard.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { DashboardComponent } from './dashboard.component';
4 |
5 | describe('DashboardComponent', () => {
6 | let component: DashboardComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ DashboardComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(DashboardComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/ui/src/app/dashboard/dashboard.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Http } from "@angular/http";
3 | import { Observable } from "rxjs/Observable";
4 | import { WalletService } from "../wallet.service";
5 |
6 | @Component({
7 | selector: 'app-dashboard',
8 | templateUrl: './dashboard.component.html',
9 | styleUrls: ['./dashboard.component.css']
10 | })
11 | export class DashboardComponent implements OnInit {
12 |
13 | public coins: any;
14 |
15 | public constructor(private http: Http, private wallet: WalletService) {
16 | this.coins = [];
17 | }
18 |
19 | public ngOnInit() {
20 | this.wallet.getAddresses()
21 | .subscribe(result => {
22 | this.coins = result.coins;
23 | }, error => {
24 | console.error(error);
25 | });
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/ui/src/app/generate/generate.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/app/generate/generate.component.css
--------------------------------------------------------------------------------
/ui/src/app/generate/generate.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Create a Wallet
7 |
8 |
9 |
10 | Your wallet contains all your secret keys and private keys. It will be encrypted, but should
11 | be stored in a safe place. Losing your wallet will result in your coins being lost forever.
12 |
13 |
Backup your wallet and store it in a safe place.
14 |
15 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/ui/src/app/generate/generate.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { GenerateComponent } from './generate.component';
4 |
5 | describe('GenerateComponent', () => {
6 | let component: GenerateComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ GenerateComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(GenerateComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/ui/src/app/generate/generate.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Headers, RequestOptions, Http } from "@angular/http";
3 | import { Router } from "@angular/router";
4 | import { WalletService } from "../wallet.service";
5 |
6 | @Component({
7 | selector: 'app-generate',
8 | templateUrl: './generate.component.html',
9 | styleUrls: ['./generate.component.css']
10 | })
11 | export class GenerateComponent implements OnInit {
12 |
13 | public input: any;
14 |
15 | public constructor(private http: Http, private router: Router, private wallet: WalletService) {
16 | this.input = {
17 | "password": "",
18 | "confirmPassword": ""
19 | };
20 | }
21 |
22 | public ngOnInit() { }
23 |
24 | public create() {
25 | if(this.input.password != "" && this.input.confirmPassword != "") {
26 | if(this.input.password == this.input.confirmPassword) {
27 | this.wallet.create(this.input.password)
28 | .subscribe(result => {
29 | this.router.navigate(["/unlock"]);
30 | });
31 | } else {
32 | console.error("The passwords to not match");
33 | }
34 | } else {
35 | console.error("All fields must be filled");
36 | }
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/ui/src/app/import/import.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/app/import/import.component.css
--------------------------------------------------------------------------------
/ui/src/app/import/import.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Import Coins
7 |
8 |
9 |
10 | Import private and secret keys into your encrypted wallet.
11 |
12 |
13 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/ui/src/app/import/import.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ImportComponent } from './import.component';
4 |
5 | describe('ImportComponent', () => {
6 | let component: ImportComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ ImportComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(ImportComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/ui/src/app/import/import.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router } from "@angular/router";
3 | import { WalletService } from "../wallet.service";
4 |
5 | @Component({
6 | selector: 'app-import',
7 | templateUrl: './import.component.html',
8 | styleUrls: ['./import.component.css']
9 | })
10 | export class ImportComponent implements OnInit {
11 |
12 |
13 | public input: any;
14 | public types: Array;
15 |
16 | public constructor(private router: Router, private wallet: WalletService) {
17 | this.input = {
18 | "type": "",
19 | "secret": ""
20 | }
21 | }
22 |
23 | public ngOnInit() { }
24 |
25 | public import() {
26 | if(this.input.type != "") {
27 | this.wallet.import(this.input.type, this.input.secret)
28 | .subscribe(result => {
29 | this.router.navigate(["/dashboard"]);
30 | }, error => {
31 | console.error(error);
32 | });
33 | }
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/ui/src/app/transaction/transaction.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/app/transaction/transaction.component.css
--------------------------------------------------------------------------------
/ui/src/app/transaction/transaction.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Create Transaction
7 |
8 |
9 |
10 | Create a signed transaction to be broadcasted on a blockchain network.
11 |
12 |
13 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/ui/src/app/transaction/transaction.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { TransactionComponent } from './transaction.component';
4 |
5 | describe('TransactionComponent', () => {
6 | let component: TransactionComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ TransactionComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(TransactionComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/ui/src/app/transaction/transaction.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router, ActivatedRoute } from "@angular/router";
3 | import { WalletService } from "../wallet.service";
4 |
5 | @Component({
6 | selector: 'app-transaction',
7 | templateUrl: './transaction.component.html',
8 | styleUrls: ['./transaction.component.css']
9 | })
10 | export class TransactionComponent implements OnInit {
11 |
12 | public input: any;
13 | public signedTx: string;
14 |
15 | public constructor(private router: Router, private route: ActivatedRoute, private wallet: WalletService) {
16 | this.input = {
17 | "sourceAddress": "",
18 | "destinationAddress": "",
19 | "amount": "1000",
20 | "txId": ""
21 | }
22 | this.signedTx = "";
23 | }
24 |
25 | public ngOnInit() {
26 | this.route.params.subscribe(params => {
27 | this.input.sourceAddress = params["source"];
28 | });
29 | }
30 |
31 | public create() {
32 | if(this.input.sourceAddress != "" && this.input.destinationAddress != "" && this.input.txId != "" && this.input.amount != "") {
33 | this.wallet.createTransaction(this.input.sourceAddress, this.input.destinationAddress, parseInt(this.input.amount), this.input.txId)
34 | .subscribe(result => {
35 | this.signedTx = result.signedtx;
36 | });
37 | } else {
38 | console.log("All fields are required");
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/ui/src/app/unlock/unlock.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/app/unlock/unlock.component.css
--------------------------------------------------------------------------------
/ui/src/app/unlock/unlock.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Welcome to Open Ledger Micro
7 |
8 |
9 |
10 | Your wallet and all your private keys and secret keys are encrypted.
11 | To continue, it must be unlocked.
12 |
13 |
14 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/ui/src/app/unlock/unlock.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { UnlockComponent } from './unlock.component';
4 |
5 | describe('UnlockComponent', () => {
6 | let component: UnlockComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ UnlockComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(UnlockComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/ui/src/app/unlock/unlock.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { Router } from "@angular/router";
3 | import { WalletService } from "../wallet.service";
4 |
5 | @Component({
6 | selector: 'app-unlock',
7 | templateUrl: './unlock.component.html',
8 | styleUrls: ['./unlock.component.css']
9 | })
10 | export class UnlockComponent implements OnInit {
11 |
12 | public input: any;
13 |
14 | public constructor(private router: Router, private wallet: WalletService) {
15 | this.input = {
16 | password: ""
17 | };
18 | }
19 |
20 | public ngOnInit() { }
21 |
22 | public unlock() {
23 | if(this.input.password == "") {
24 | return console.log("A password is required");
25 | }
26 | this.wallet.authenticate(this.input.password)
27 | .subscribe(result => {
28 | this.router.navigate(["/dashboard"]);
29 | }, error => {
30 | console.error(error.message);
31 | });
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/ui/src/app/wallet.service.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { WalletService } from './wallet.service';
4 |
5 | describe('WalletService', () => {
6 | beforeEach(() => {
7 | TestBed.configureTestingModule({
8 | providers: [WalletService]
9 | });
10 | });
11 |
12 | it('should be created', inject([WalletService], (service: WalletService) => {
13 | expect(service).toBeTruthy();
14 | }));
15 | });
16 |
--------------------------------------------------------------------------------
/ui/src/app/wallet.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, EventEmitter } from '@angular/core';
2 | import { Headers, RequestOptions, Http } from "@angular/http";
3 | import { Router } from "@angular/router";
4 | import { Observable } from "rxjs/Observable";
5 | import "rxjs/add/operator/map";
6 | import "rxjs/add/operator/do";
7 |
8 | @Injectable()
9 | export class WalletService {
10 |
11 | private host: string;
12 | private authenticated: EventEmitter = new EventEmitter();
13 | public password: string;
14 |
15 | public constructor(private http: Http, private router: Router) {
16 | this.host = "http://localhost:12345/api";
17 | //this.host = "http://coin.local:12345/api";
18 | this.password = "";
19 | }
20 |
21 | public isAuthenticated() {
22 | return this.authenticated;
23 | }
24 |
25 | public authenticate(password: string) {
26 | let headers = new Headers({ "content-type": "application/json" });
27 | let options = new RequestOptions({ headers: headers });
28 | return this.http.post(this.host + "/authenticate?key=" + password, null, options)
29 | .map(result => result.json())
30 | .do(result => {
31 | if(!result.authenticated) {
32 | throw new Error("The wallet does not exist or the password is incorrect");
33 | } else {
34 | this.password = password;
35 | this.authenticated.emit(true);
36 | }
37 | });
38 | }
39 |
40 | public disconnect() {
41 | this.password = "";
42 | this.authenticated.emit(false);
43 | }
44 |
45 | public create(password: string) {
46 | let headers = new Headers({ "content-type": "application/json" });
47 | let options = new RequestOptions({ headers: headers });
48 | return this.http.post(this.host + "/wallet?key=" + password, null, options)
49 | .map(result => result.json());
50 | }
51 |
52 | public dump() {
53 | return this.http.get(this.host + "/wallet?key=" + this.password)
54 | .map(result => result.json());
55 | }
56 |
57 | public getAddresses() {
58 | return this.http.get(this.host + "/addresses?key=" + this.password)
59 | .map(result => result.json());
60 | }
61 |
62 | public import(symbol: string, secret: string) {
63 | let headers = new Headers({ "content-type": "application/json" });
64 | let options = new RequestOptions({ headers: headers });
65 | if(secret != "") {
66 | return this.http.post(this.host + "/import-coin?key=" + this.password, JSON.stringify({ "symbol": symbol, "wif": secret }), options)
67 | .map(result => result.json());
68 | } else {
69 | return this.http.post(this.host + "/create-coin?key=" + this.password, JSON.stringify({ "symbol": symbol }), options)
70 | .map(result => result.json());
71 | }
72 | }
73 |
74 | public backup() {
75 | return this.http.get(this.host + "/backup")
76 | .map(result => result.text());
77 | }
78 |
79 | public createTransaction(sender: string, recipient: string, amount: number, txHash: string) {
80 | let headers = new Headers({ "content-type": "application/json" });
81 | let options = new RequestOptions({ headers: headers });
82 | return this.http.post(this.host + "/transaction?key=" + this.password, JSON.stringify({ "source_address": sender, "destination_address": recipient, "amount": amount, "txid": txHash }), options)
83 | .map(result => result.json());
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/ui/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/.gitkeep
--------------------------------------------------------------------------------
/ui/src/assets/css/bootstrap-select.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap-select v1.12.4 (http://silviomoreto.github.io/bootstrap-select)
3 | *
4 | * Copyright 2013-2017 bootstrap-select
5 | * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
6 | */select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\9}.bootstrap-select>.dropdown-toggle{width:100%;padding-right:25px;z-index:1}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2}.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{z-index:auto}.bootstrap-select.form-control.input-group-btn:not(:first-child):not(:last-child)>.btn{border-radius:0}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.btn-group.dropdown-menu-right,.bootstrap-select.btn-group[class*=col-].dropdown-menu-right,.row .bootstrap-select.btn-group[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group{margin-bottom:0}.form-group-lg .bootstrap-select.btn-group.form-control,.form-group-sm .bootstrap-select.btn-group.form-control{padding:0}.form-group-lg .bootstrap-select.btn-group.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.btn-group.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.form-inline .bootstrap-select.btn-group .form-control{width:100%}.bootstrap-select.btn-group.disabled,.bootstrap-select.btn-group>.disabled{cursor:not-allowed}.bootstrap-select.btn-group.disabled:focus,.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group.bs-container{position:absolute;height:0!important;padding:0!important}.bootstrap-select.btn-group.bs-container .dropdown-menu{z-index:1060}.bootstrap-select.btn-group .dropdown-toggle .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li.active small{color:#fff}.bootstrap-select.btn-group .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select.btn-group .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select.btn-group .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select.btn-group .dropdown-menu li a span.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.btn-group.fit-width .dropdown-toggle .filter-option{position:static}.bootstrap-select.btn-group.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{position:absolute;display:inline-block;right:15px;margin-top:5px}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none}
--------------------------------------------------------------------------------
/ui/src/assets/css/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}
6 | /*# sourceMappingURL=bootstrap-theme.min.css.map */
--------------------------------------------------------------------------------
/ui/src/assets/css/bootstrap-theme.min.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":";;;;AAmBA,YAAA,aAAA,UAAA,aAAA,aAAA,aAME,YAAA,EAAA,KAAA,EAAA,eC2CA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBDvCR,mBAAA,mBAAA,oBAAA,oBAAA,iBAAA,iBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBAAA,oBCsCA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBDlCR,qBAAA,sBAAA,sBAAA,uBAAA,mBAAA,oBAAA,sBAAA,uBAAA,sBAAA,uBAAA,sBAAA,uBAAA,+BAAA,gCAAA,6BAAA,gCAAA,gCAAA,gCCiCA,mBAAA,KACQ,WAAA,KDlDV,mBAAA,oBAAA,iBAAA,oBAAA,oBAAA,oBAuBI,YAAA,KAyCF,YAAA,YAEE,iBAAA,KAKJ,aErEI,YAAA,EAAA,IAAA,EAAA,KACA,iBAAA,iDACA,iBAAA,4CAAA,iBAAA,qEAEA,iBAAA,+CCnBF,OAAA,+GH4CA,OAAA,0DACA,kBAAA,SAuC2C,aAAA,QAA2B,aAAA,KArCtE,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAgBN,aEtEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAiBN,aEvEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAkBN,UExEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,gBAAA,gBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,iBAAA,iBAEE,iBAAA,QACA,aAAA,QAMA,mBAAA,0BAAA,yBAAA,0BAAA,yBAAA,yBAAA,oBAAA,2BAAA,0BAAA,2BAAA,0BAAA,0BAAA,6BAAA,oCAAA,mCAAA,oCAAA,mCAAA,mCAME,iBAAA,QACA,iBAAA,KAmBN,aEzEI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,mBAAA,mBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,oBAAA,oBAEE,iBAAA,QACA,aAAA,QAMA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,uBAAA,8BAAA,6BAAA,8BAAA,6BAAA,6BAAA,gCAAA,uCAAA,sCAAA,uCAAA,sCAAA,sCAME,iBAAA,QACA,iBAAA,KAoBN,YE1EI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDAEA,OAAA,+GCnBF,OAAA,0DH4CA,kBAAA,SACA,aAAA,QAEA,kBAAA,kBAEE,iBAAA,QACA,oBAAA,EAAA,MAGF,mBAAA,mBAEE,iBAAA,QACA,aAAA,QAMA,qBAAA,4BAAA,2BAAA,4BAAA,2BAAA,2BAAA,sBAAA,6BAAA,4BAAA,6BAAA,4BAAA,4BAAA,+BAAA,sCAAA,qCAAA,sCAAA,qCAAA,qCAME,iBAAA,QACA,iBAAA,KA2BN,eAAA,WClCE,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBD2CV,0BAAA,0BE3FI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GF0FF,kBAAA,SAEF,yBAAA,+BAAA,+BEhGI,iBAAA,QACA,iBAAA,oDACA,iBAAA,+CAAA,iBAAA,wEACA,iBAAA,kDACA,OAAA,+GFgGF,kBAAA,SASF,gBE7GI,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SH+HA,cAAA,ICjEA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,iBD6DV,sCAAA,oCE7GI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBD0EV,cAAA,iBAEE,YAAA,EAAA,IAAA,EAAA,sBAIF,gBEhII,iBAAA,iDACA,iBAAA,4CACA,iBAAA,qEAAA,iBAAA,+CACA,OAAA,+GACA,OAAA,0DCnBF,kBAAA,SHkJA,cAAA,IAHF,sCAAA,oCEhII,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SD2CF,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBDgFV,8BAAA,iCAYI,YAAA,EAAA,KAAA,EAAA,gBAKJ,qBAAA,kBAAA,mBAGE,cAAA,EAqBF,yBAfI,mDAAA,yDAAA,yDAGE,MAAA,KE7JF,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,UFqKJ,OACE,YAAA,EAAA,IAAA,EAAA,qBC3HA,mBAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,MAAA,EAAA,IAAA,EAAA,sBAAA,EAAA,IAAA,IAAA,gBDsIV,eEtLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAKF,YEvLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAMF,eExLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAOF,cEzLI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF8KF,aAAA,QAeF,UEjMI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFuMJ,cE3MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFwMJ,sBE5MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyMJ,mBE7MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0MJ,sBE9MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2MJ,qBE/MI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF+MJ,sBElLI,iBAAA,yKACA,iBAAA,oKACA,iBAAA,iKFyLJ,YACE,cAAA,IC9KA,mBAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,EAAA,IAAA,IAAA,iBDgLV,wBAAA,8BAAA,8BAGE,YAAA,EAAA,KAAA,EAAA,QEnOE,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFiOF,aAAA,QALF,+BAAA,qCAAA,qCAQI,YAAA,KAUJ,OCnME,mBAAA,EAAA,IAAA,IAAA,gBACQ,WAAA,EAAA,IAAA,IAAA,gBD4MV,8BE5PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFyPJ,8BE7PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF0PJ,8BE9PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF2PJ,2BE/PI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF4PJ,8BEhQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SF6PJ,6BEjQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFoQJ,MExQI,iBAAA,oDACA,iBAAA,+CACA,iBAAA,wEAAA,iBAAA,kDACA,OAAA,+GACA,kBAAA,SFsQF,aAAA,QC3NA,mBAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA,qBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,gBAAA,EAAA,IAAA,EAAA","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]}
--------------------------------------------------------------------------------
/ui/src/assets/css/fa-brands.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 5.0.1 by @fontawesome - http://fontawesome.com
3 | * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | */
5 | @font-face{font-family:Font Awesome\ 5 Brands;font-style:normal;font-weight:400;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:Font Awesome\ 5 Brands}
--------------------------------------------------------------------------------
/ui/src/assets/css/fa-regular.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 5.0.1 by @fontawesome - http://fontawesome.com
3 | * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | */
5 | @font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:400;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:Font Awesome\ 5 Free;font-weight:400}
--------------------------------------------------------------------------------
/ui/src/assets/css/fa-solid.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 5.0.1 by @fontawesome - http://fontawesome.com
3 | * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | */
5 | @font-face{font-family:Font Awesome\ 5 Free;font-style:normal;font-weight:900;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:Font Awesome\ 5 Free;font-weight:900}
--------------------------------------------------------------------------------
/ui/src/assets/css/fontawesome.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 5.0.1 by @fontawesome - http://fontawesome.com
3 | * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | */
5 | .fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:a 2s infinite linear;animation:a 2s infinite linear}.fa-pulse{-webkit-animation:a 1s infinite steps(8);animation:a 1s infinite steps(8)}@-webkit-keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-amazon:before{content:"\f270"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-aws:before{content:"\f375"}.fa-backward:before{content:"\f04a"}.fa-balance-scale:before{content:"\f24e"}.fa-ban:before{content:"\f05e"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bicycle:before{content:"\f206"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blind:before{content:"\f29d"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-braille:before{content:"\f2a1"}.fa-briefcase:before{content:"\f0b1"}.fa-btc:before{content:"\f15a"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-car:before{content:"\f1b9"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-certificate:before{content:"\f0a3"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-square:before{content:"\f14a"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-clipboard:before{content:"\f328"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comments:before{content:"\f086"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-credit-card:before{content:"\f09d"}.fa-crop:before{content:"\f125"}.fa-crosshairs:before{content:"\f05b"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-deviantart:before{content:"\f1bd"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dollar-sign:before{content:"\f155"}.fa-dot-circle:before{content:"\f192"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drupal:before{content:"\f1a9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-eject:before{content:"\f052"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-excel:before{content:"\f1c3"}.fa-file-image:before{content:"\f1c5"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fire:before{content:"\f06d"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-order:before{content:"\f2b0"}.fa-firstdraft:before{content:"\f3a1"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frown:before{content:"\f119"}.fa-futbol:before{content:"\f1e3"}.fa-gamepad:before{content:"\f11b"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-gift:before{content:"\f06b"}.fa-git:before{content:"\f1d3"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-martini:before{content:"\f000"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-gofore:before{content:"\f3a7"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-handshake:before{content:"\f2b5"}.fa-hashtag:before{content:"\f292"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-heart:before{content:"\f004"}.fa-heartbeat:before{content:"\f21e"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hospital:before{content:"\f0f8"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-houzz:before{content:"\f27c"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-internet-explorer:before{content:"\f26b"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-jenkins:before{content:"\f3b6"}.fa-joget:before{content:"\f3b7"}.fa-joomla:before{content:"\f1aa"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-key:before{content:"\f084"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-male:before{content:"\f183"}.fa-map:before{content:"\f279"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-maxcdn:before{content:"\f136"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-meh:before{content:"\f11a"}.fa-mercury:before{content:"\f223"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-moon:before{content:"\f186"}.fa-motorcycle:before{content:"\f21c"}.fa-mouse-pointer:before{content:"\f245"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nintendo-switch:before{content:"\f418"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-paint-brush:before{content:"\f1fc"}.fa-palfed:before{content:"\f3d8"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-paragraph:before{content:"\f1dd"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-percent:before{content:"\f295"}.fa-periscope:before{content:"\f3da"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phone:before{content:"\f095"}.fa-phone-square:before{content:"\f098"}.fa-phone-volume:before{content:"\f2a0"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-plane:before{content:"\f072"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-print:before{content:"\f02f"}.fa-product-hunt:before{content:"\f288"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-random:before{content:"\f074"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-rebel:before{content:"\f1d0"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-rendact:before{content:"\f3e4"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-resolving:before{content:"\f3e7"}.fa-retweet:before{content:"\f079"}.fa-road:before{content:"\f018"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-rupee-sign:before{content:"\f156"}.fa-safari:before{content:"\f267"}.fa-sass:before{content:"\f41e"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-scribd:before{content:"\f28a"}.fa-search:before{content:"\f002"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shower:before{content:"\f2cc"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowflake:before{content:"\f2dc"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-spinner:before{content:"\f110"}.fa-spotify:before{content:"\f1bc"}.fa-square:before{content:"\f0c8"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-star:before{content:"\f005"}.fa-star-half:before{content:"\f089"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-strava:before{content:"\f428"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-table:before{content:"\f0ce"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-trademark:before{content:"\f25c"}.fa-train:before{content:"\f238"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-circle:before{content:"\f2bd"}.fa-user-md:before{content:"\f0f0"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-volume-down:before{content:"\f027"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vuejs:before{content:"\f41f"}.fa-weibo:before{content:"\f18a"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wrench:before{content:"\f0ad"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}
--------------------------------------------------------------------------------
/ui/src/assets/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/ui/src/assets/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/ui/src/assets/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/ui/src/assets/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/ui/src/assets/js/bootstrap-select.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap-select v1.12.4 (http://silviomoreto.github.io/bootstrap-select)
3 | *
4 | * Copyright 2013-2017 bootstrap-select
5 | * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE)
6 | */
7 | !function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof module&&module.exports?module.exports=b(require("jquery")):b(a.jQuery)}(this,function(a){!function(a){"use strict";function b(b){var c=[{re:/[\xC0-\xC6]/g,ch:"A"},{re:/[\xE0-\xE6]/g,ch:"a"},{re:/[\xC8-\xCB]/g,ch:"E"},{re:/[\xE8-\xEB]/g,ch:"e"},{re:/[\xCC-\xCF]/g,ch:"I"},{re:/[\xEC-\xEF]/g,ch:"i"},{re:/[\xD2-\xD6]/g,ch:"O"},{re:/[\xF2-\xF6]/g,ch:"o"},{re:/[\xD9-\xDC]/g,ch:"U"},{re:/[\xF9-\xFC]/g,ch:"u"},{re:/[\xC7-\xE7]/g,ch:"c"},{re:/[\xD1]/g,ch:"N"},{re:/[\xF1]/g,ch:"n"}];return a.each(c,function(){b=b?b.replace(this.re,this.ch):""}),b}function c(b){var c=arguments,d=b;[].shift.apply(c);var e,f=this.each(function(){var b=a(this);if(b.is("select")){var f=b.data("selectpicker"),g="object"==typeof d&&d;if(f){if(g)for(var h in g)g.hasOwnProperty(h)&&(f.options[h]=g[h])}else{var i=a.extend({},l.DEFAULTS,a.fn.selectpicker.defaults||{},b.data(),g);i.template=a.extend({},l.DEFAULTS.template,a.fn.selectpicker.defaults?a.fn.selectpicker.defaults.template:{},b.data().template,g.template),b.data("selectpicker",f=new l(this,i))}"string"==typeof d&&(e=f[d]instanceof Function?f[d].apply(f,c):f.options[d])}});return"undefined"!=typeof e?e:f}String.prototype.includes||!function(){var a={}.toString,b=function(){try{var a={},b=Object.defineProperty,c=b(a,a,a)&&b}catch(a){}return c}(),c="".indexOf,d=function(b){if(null==this)throw new TypeError;var d=String(this);if(b&&"[object RegExp]"==a.call(b))throw new TypeError;var e=d.length,f=String(b),g=f.length,h=arguments.length>1?arguments[1]:void 0,i=h?Number(h):0;i!=i&&(i=0);var j=Math.min(Math.max(i,0),e);return!(g+j>e)&&c.call(d,f,i)!=-1};b?b(String.prototype,"includes",{value:d,configurable:!0,writable:!0}):String.prototype.includes=d}(),String.prototype.startsWith||!function(){var a=function(){try{var a={},b=Object.defineProperty,c=b(a,a,a)&&b}catch(a){}return c}(),b={}.toString,c=function(a){if(null==this)throw new TypeError;var c=String(this);if(a&&"[object RegExp]"==b.call(a))throw new TypeError;var d=c.length,e=String(a),f=e.length,g=arguments.length>1?arguments[1]:void 0,h=g?Number(g):0;h!=h&&(h=0);var i=Math.min(Math.max(h,0),d);if(f+i>d)return!1;for(var j=-1;++j":">",'"':""","'":"'","`":"`"},h={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},i=function(a){var b=function(b){return a[b]},c="(?:"+Object.keys(a).join("|")+")",d=RegExp(c),e=RegExp(c,"g");return function(a){return a=null==a?"":""+a,d.test(a)?a.replace(e,b):a}},j=i(g),k=i(h),l=function(b,c){d.useDefault||(a.valHooks.select.set=d._set,d.useDefault=!0),this.$element=a(b),this.$newElement=null,this.$button=null,this.$menu=null,this.$lis=null,this.options=c,null===this.options.title&&(this.options.title=this.$element.attr("title"));var e=this.options.windowPadding;"number"==typeof e&&(this.options.windowPadding=[e,e,e,e]),this.val=l.prototype.val,this.render=l.prototype.render,this.refresh=l.prototype.refresh,this.setStyle=l.prototype.setStyle,this.selectAll=l.prototype.selectAll,this.deselectAll=l.prototype.deselectAll,this.destroy=l.prototype.destroy,this.remove=l.prototype.remove,this.show=l.prototype.show,this.hide=l.prototype.hide,this.init()};l.VERSION="1.12.4",l.DEFAULTS={noneSelectedText:"Nothing selected",noneResultsText:"No results matched {0}",countSelectedText:function(a,b){return 1==a?"{0} item selected":"{0} items selected"},maxOptionsText:function(a,b){return[1==a?"Limit reached ({n} item max)":"Limit reached ({n} items max)",1==b?"Group limit reached ({n} item max)":"Group limit reached ({n} items max)"]},selectAllText:"Select All",deselectAllText:"Deselect All",doneButton:!1,doneButtonText:"Close",multipleSeparator:", ",styleBase:"btn",style:"btn-default",size:"auto",title:null,selectedTextFormat:"values",width:!1,container:!1,hideDisabled:!1,showSubtext:!1,showIcon:!0,showContent:!0,dropupAuto:!0,header:!1,liveSearch:!1,liveSearchPlaceholder:null,liveSearchNormalize:!1,liveSearchStyle:"contains",actionsBox:!1,iconBase:"glyphicon",tickIcon:"glyphicon-ok",showTick:!1,template:{caret:' '},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0},l.prototype={constructor:l,init:function(){var b=this,c=this.$element.attr("id");this.$element.addClass("bs-select-hidden"),this.liObj={},this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$newElement=this.createView(),this.$element.after(this.$newElement).appendTo(this.$newElement),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(".dropdown-menu"),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),this.$element.removeClass("bs-select-hidden"),this.options.dropdownAlignRight===!0&&this.$menu.addClass("dropdown-menu-right"),"undefined"!=typeof c&&(this.$button.attr("data-id",c),a('label[for="'+c+'"]').click(function(a){a.preventDefault(),b.$button.focus()})),this.checkDisabled(),this.clickListener(),this.options.liveSearch&&this.liveSearchListener(),this.render(),this.setStyle(),this.setWidth(),this.options.container&&this.selectPosition(),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(a){b.$menuInner.attr("aria-expanded",!1),b.$element.trigger("hide.bs.select",a)},"hidden.bs.dropdown":function(a){b.$element.trigger("hidden.bs.select",a)},"show.bs.dropdown":function(a){b.$menuInner.attr("aria-expanded",!0),b.$element.trigger("show.bs.select",a)},"shown.bs.dropdown":function(a){b.$element.trigger("shown.bs.select",a)}}),b.$element[0].hasAttribute("required")&&this.$element.on("invalid",function(){b.$button.addClass("bs-invalid"),b.$element.on({"focus.bs.select":function(){b.$button.focus(),b.$element.off("focus.bs.select")},"shown.bs.select":function(){b.$element.val(b.$element.val()).off("shown.bs.select")},"rendered.bs.select":function(){this.validity.valid&&b.$button.removeClass("bs-invalid"),b.$element.off("rendered.bs.select")}}),b.$button.on("blur.bs.select",function(){b.$element.focus().blur(),b.$button.off("blur.bs.select")})}),setTimeout(function(){b.$element.trigger("loaded.bs.select")})},createDropdown:function(){var b=this.multiple||this.options.showTick?" show-tick":"",c=this.$element.parent().hasClass("input-group")?" input-group-btn":"",d=this.autofocus?" autofocus":"",e=this.options.header?'× '+this.options.header+"
":"",f=this.options.liveSearch?'
':"",g=this.multiple&&this.options.actionsBox?''+this.options.selectAllText+' '+this.options.deselectAllText+"
":"",h=this.multiple&&this.options.doneButton?'":"",i=' '+this.options.template.caret+'
";return a(i)},createView:function(){var a=this.createDropdown(),b=this.createLi();return a.find("ul")[0].innerHTML=b,a},reloadLi:function(){var a=this.createLi();this.$menuInner[0].innerHTML=a},createLi:function(){var c=this,d=[],e=0,f=document.createElement("option"),g=-1,h=function(a,b,c,d){return""+a+" "},i=function(d,e,f,g){return''+d+' '};if(this.options.title&&!this.multiple&&(g--,!this.$element.find(".bs-title-option").length)){var k=this.$element[0];f.className="bs-title-option",f.innerHTML=this.options.title,f.value="",k.insertBefore(f,k.firstChild);var l=a(k.options[k.selectedIndex]);void 0===l.attr("selected")&&void 0===this.$element.data("selected")&&(f.selected=!0)}var m=this.$element.find("option");return m.each(function(b){var f=a(this);if(g++,!f.hasClass("bs-title-option")){var k,l=this.className||"",n=j(this.style.cssText),o=f.data("content")?f.data("content"):f.html(),p=f.data("tokens")?f.data("tokens"):null,q="undefined"!=typeof f.data("subtext")?''+f.data("subtext")+" ":"",r="undefined"!=typeof f.data("icon")?' ':"",s=f.parent(),t="OPTGROUP"===s[0].tagName,u=t&&s[0].disabled,v=this.disabled||u;if(""!==r&&v&&(r=""+r+" "),c.options.hideDisabled&&(v&&!t||u))return k=f.data("prevHiddenIndex"),f.next().data("prevHiddenIndex",void 0!==k?k:b),void g--;if(f.data("content")||(o=r+''+o+q+" "),t&&f.data("divider")!==!0){if(c.options.hideDisabled&&v){if(void 0===s.data("allOptionsDisabled")){var w=s.children();s.data("allOptionsDisabled",w.filter(":disabled").length===w.length)}if(s.data("allOptionsDisabled"))return void g--}var x=" "+s[0].className||"";if(0===f.index()){e+=1;var y=s[0].label,z="undefined"!=typeof s.data("subtext")?''+s.data("subtext")+" ":"",A=s.data("icon")?' ':"";y=A+''+j(y)+z+" ",0!==b&&d.length>0&&(g++,d.push(h("",null,"divider",e+"div"))),g++,d.push(h(y,null,"dropdown-header"+x,e))}if(c.options.hideDisabled&&v)return void g--;d.push(h(i(o,"opt "+l+x,n,p),b,"",e))}else if(f.data("divider")===!0)d.push(h("",b,"divider"));else if(f.data("hidden")===!0)k=f.data("prevHiddenIndex"),f.next().data("prevHiddenIndex",void 0!==k?k:b),d.push(h(i(o,l,n,p),b,"hidden is-hidden"));else{var B=this.previousElementSibling&&"OPTGROUP"===this.previousElementSibling.tagName;if(!B&&c.options.hideDisabled&&(k=f.data("prevHiddenIndex"),void 0!==k)){var C=m.eq(k)[0].previousElementSibling;C&&"OPTGROUP"===C.tagName&&!C.disabled&&(B=!0)}B&&(g++,d.push(h("",null,"divider",e+"div"))),d.push(h(i(o,l,n,p),b))}c.liObj[b]=g}}),this.multiple||0!==this.$element.find("option:selected").length||this.options.title||this.$element.find("option").eq(0).prop("selected",!0).attr("selected","selected"),d.join("")},findLis:function(){return null==this.$lis&&(this.$lis=this.$menu.find("li")),this.$lis},render:function(b){var c,d=this,e=this.$element.find("option");b!==!1&&e.each(function(a){var b=d.findLis().eq(d.liObj[a]);d.setDisabled(a,this.disabled||"OPTGROUP"===this.parentNode.tagName&&this.parentNode.disabled,b),d.setSelected(a,this.selected,b)}),this.togglePlaceholder(),this.tabIndex();var f=e.map(function(){if(this.selected){if(d.options.hideDisabled&&(this.disabled||"OPTGROUP"===this.parentNode.tagName&&this.parentNode.disabled))return;var b,c=a(this),e=c.data("icon")&&d.options.showIcon?' ':"";return b=d.options.showSubtext&&c.data("subtext")&&!d.multiple?' '+c.data("subtext")+" ":"","undefined"!=typeof c.attr("title")?c.attr("title"):c.data("content")&&d.options.showContent?c.data("content").toString():e+c.html()+b}}).toArray(),g=this.multiple?f.join(this.options.multipleSeparator):f[0];if(this.multiple&&this.options.selectedTextFormat.indexOf("count")>-1){var h=this.options.selectedTextFormat.split(">");if(h.length>1&&f.length>h[1]||1==h.length&&f.length>=2){c=this.options.hideDisabled?", [disabled]":"";var i=e.not('[data-divider="true"], [data-hidden="true"]'+c).length,j="function"==typeof this.options.countSelectedText?this.options.countSelectedText(f.length,i):this.options.countSelectedText;g=j.replace("{0}",f.length.toString()).replace("{1}",i.toString())}}void 0==this.options.title&&(this.options.title=this.$element.attr("title")),"static"==this.options.selectedTextFormat&&(g=this.options.title),g||(g="undefined"!=typeof this.options.title?this.options.title:this.options.noneSelectedText),this.$button.attr("title",k(a.trim(g.replace(/<[^>]*>?/g,"")))),this.$button.children(".filter-option").html(g),this.$element.trigger("rendered.bs.select")},setStyle:function(a,b){this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,""));var c=a?a:this.options.style;"add"==b?this.$button.addClass(c):"remove"==b?this.$button.removeClass(c):(this.$button.removeClass(this.options.style),this.$button.addClass(c))},liHeight:function(b){if(b||this.options.size!==!1&&!this.sizeInfo){var c=document.createElement("div"),d=document.createElement("div"),e=document.createElement("ul"),f=document.createElement("li"),g=document.createElement("li"),h=document.createElement("a"),i=document.createElement("span"),j=this.options.header&&this.$menu.find(".popover-title").length>0?this.$menu.find(".popover-title")[0].cloneNode(!0):null,k=this.options.liveSearch?document.createElement("div"):null,l=this.options.actionsBox&&this.multiple&&this.$menu.find(".bs-actionsbox").length>0?this.$menu.find(".bs-actionsbox")[0].cloneNode(!0):null,m=this.options.doneButton&&this.multiple&&this.$menu.find(".bs-donebutton").length>0?this.$menu.find(".bs-donebutton")[0].cloneNode(!0):null;if(i.className="text",c.className=this.$menu[0].parentNode.className+" open",d.className="dropdown-menu open",e.className="dropdown-menu inner",f.className="divider",i.appendChild(document.createTextNode("Inner text")),h.appendChild(i),g.appendChild(h),e.appendChild(g),e.appendChild(f),j&&d.appendChild(j),k){var n=document.createElement("input");k.className="bs-searchbox",n.className="form-control",k.appendChild(n),d.appendChild(k)}l&&d.appendChild(l),d.appendChild(e),m&&d.appendChild(m),c.appendChild(d),document.body.appendChild(c);var o=h.offsetHeight,p=j?j.offsetHeight:0,q=k?k.offsetHeight:0,r=l?l.offsetHeight:0,s=m?m.offsetHeight:0,t=a(f).outerHeight(!0),u="function"==typeof getComputedStyle&&getComputedStyle(d),v=u?null:a(d),w={vert:parseInt(u?u.paddingTop:v.css("paddingTop"))+parseInt(u?u.paddingBottom:v.css("paddingBottom"))+parseInt(u?u.borderTopWidth:v.css("borderTopWidth"))+parseInt(u?u.borderBottomWidth:v.css("borderBottomWidth")),horiz:parseInt(u?u.paddingLeft:v.css("paddingLeft"))+parseInt(u?u.paddingRight:v.css("paddingRight"))+parseInt(u?u.borderLeftWidth:v.css("borderLeftWidth"))+parseInt(u?u.borderRightWidth:v.css("borderRightWidth"))},x={vert:w.vert+parseInt(u?u.marginTop:v.css("marginTop"))+parseInt(u?u.marginBottom:v.css("marginBottom"))+2,horiz:w.horiz+parseInt(u?u.marginLeft:v.css("marginLeft"))+parseInt(u?u.marginRight:v.css("marginRight"))+2};document.body.removeChild(c),this.sizeInfo={liHeight:o,headerHeight:p,searchHeight:q,actionsHeight:r,doneButtonHeight:s,dividerHeight:t,menuPadding:w,menuExtras:x}}},setSize:function(){if(this.findLis(),this.liHeight(),this.options.header&&this.$menu.css("padding-top",0),this.options.size!==!1){var b,c,d,e,f,g,h,i,j=this,k=this.$menu,l=this.$menuInner,m=a(window),n=this.$newElement[0].offsetHeight,o=this.$newElement[0].offsetWidth,p=this.sizeInfo.liHeight,q=this.sizeInfo.headerHeight,r=this.sizeInfo.searchHeight,s=this.sizeInfo.actionsHeight,t=this.sizeInfo.doneButtonHeight,u=this.sizeInfo.dividerHeight,v=this.sizeInfo.menuPadding,w=this.sizeInfo.menuExtras,x=this.options.hideDisabled?".disabled":"",y=function(){var b,c=j.$newElement.offset(),d=a(j.options.container);j.options.container&&!d.is("body")?(b=d.offset(),b.top+=parseInt(d.css("borderTopWidth")),b.left+=parseInt(d.css("borderLeftWidth"))):b={top:0,left:0};var e=j.options.windowPadding;f=c.top-b.top-m.scrollTop(),g=m.height()-f-n-b.top-e[2],h=c.left-b.left-m.scrollLeft(),i=m.width()-h-o-b.left-e[1],f-=e[0],h-=e[3]};if(y(),"auto"===this.options.size){var z=function(){var m,n=function(b,c){return function(d){return c?d.classList?d.classList.contains(b):a(d).hasClass(b):!(d.classList?d.classList.contains(b):a(d).hasClass(b))}},u=j.$menuInner[0].getElementsByTagName("li"),x=Array.prototype.filter?Array.prototype.filter.call(u,n("hidden",!1)):j.$lis.not(".hidden"),z=Array.prototype.filter?Array.prototype.filter.call(x,n("dropdown-header",!0)):x.filter(".dropdown-header");y(),b=g-w.vert,c=i-w.horiz,j.options.container?(k.data("height")||k.data("height",k.height()),d=k.data("height"),k.data("width")||k.data("width",k.width()),e=k.data("width")):(d=k.height(),e=k.width()),j.options.dropupAuto&&j.$newElement.toggleClass("dropup",f>g&&b-w.verti&&c-w.horiz3?3*p+w.vert-2:0,k.css({"max-height":b+"px",overflow:"hidden","min-height":m+q+r+s+t+"px"}),l.css({"max-height":b-q-r-s-t-v.vert+"px","overflow-y":"auto","min-height":Math.max(m-v.vert,0)+"px"})};z(),this.$searchbox.off("input.getSize propertychange.getSize").on("input.getSize propertychange.getSize",z),m.off("resize.getSize scroll.getSize").on("resize.getSize scroll.getSize",z)}else if(this.options.size&&"auto"!=this.options.size&&this.$lis.not(x).length>this.options.size){var A=this.$lis.not(".divider").not(x).children().slice(0,this.options.size).last().parent().index(),B=this.$lis.slice(0,A+1).filter(".divider").length;b=p*this.options.size+B*u+v.vert,j.options.container?(k.data("height")||k.data("height",k.height()),d=k.data("height")):d=k.height(),j.options.dropupAuto&&this.$newElement.toggleClass("dropup",f>g&&b-w.vert ');var b,c,d,e=this,f=a(this.options.container),g=function(a){e.$bsContainer.addClass(a.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass("dropup",a.hasClass("dropup")),b=a.offset(),f.is("body")?c={top:0,left:0}:(c=f.offset(),c.top+=parseInt(f.css("borderTopWidth"))-f.scrollTop(),c.left+=parseInt(f.css("borderLeftWidth"))-f.scrollLeft()),d=a.hasClass("dropup")?0:a[0].offsetHeight,e.$bsContainer.css({top:b.top-c.top+d,left:b.left-c.left,width:a[0].offsetWidth})};this.$button.on("click",function(){var b=a(this);e.isDisabled()||(g(e.$newElement),e.$bsContainer.appendTo(e.options.container).toggleClass("open",!b.hasClass("open")).append(e.$menu))}),a(window).on("resize scroll",function(){g(e.$newElement)}),this.$element.on("hide.bs.select",function(){e.$menu.data("height",e.$menu.height()),e.$bsContainer.detach()})},setSelected:function(a,b,c){c||(this.togglePlaceholder(),c=this.findLis().eq(this.liObj[a])),c.toggleClass("selected",b).find("a").attr("aria-selected",b)},setDisabled:function(a,b,c){c||(c=this.findLis().eq(this.liObj[a])),b?c.addClass("disabled").children("a").attr("href","#").attr("tabindex",-1).attr("aria-disabled",!0):c.removeClass("disabled").children("a").removeAttr("href").attr("tabindex",0).attr("aria-disabled",!1)},isDisabled:function(){return this.$element[0].disabled},checkDisabled:function(){var a=this;this.isDisabled()?(this.$newElement.addClass("disabled"),this.$button.addClass("disabled").attr("tabindex",-1).attr("aria-disabled",!0)):(this.$button.hasClass("disabled")&&(this.$newElement.removeClass("disabled"),this.$button.removeClass("disabled").attr("aria-disabled",!1)),this.$button.attr("tabindex")!=-1||this.$element.data("tabindex")||this.$button.removeAttr("tabindex")),this.$button.click(function(){return!a.isDisabled()})},togglePlaceholder:function(){var a=this.$element.val();this.$button.toggleClass("bs-placeholder",null===a||""===a||a.constructor===Array&&0===a.length)},tabIndex:function(){this.$element.data("tabindex")!==this.$element.attr("tabindex")&&this.$element.attr("tabindex")!==-98&&"-98"!==this.$element.attr("tabindex")&&(this.$element.data("tabindex",this.$element.attr("tabindex")),this.$button.attr("tabindex",this.$element.data("tabindex"))),this.$element.attr("tabindex",-98)},clickListener:function(){var b=this,c=a(document);c.data("spaceSelect",!1),this.$button.on("keyup",function(a){/(32)/.test(a.keyCode.toString(10))&&c.data("spaceSelect")&&(a.preventDefault(),c.data("spaceSelect",!1))}),this.$button.on("click",function(){b.setSize()}),this.$element.on("shown.bs.select",function(){if(b.options.liveSearch||b.multiple){if(!b.multiple){var a=b.liObj[b.$element[0].selectedIndex];if("number"!=typeof a||b.options.size===!1)return;var c=b.$lis.eq(a)[0].offsetTop-b.$menuInner[0].offsetTop;c=c-b.$menuInner[0].offsetHeight/2+b.sizeInfo.liHeight/2,b.$menuInner[0].scrollTop=c}}else b.$menuInner.find(".selected a").focus()}),this.$menuInner.on("click","li a",function(c){var d=a(this),f=d.parent().data("originalIndex"),g=b.$element.val(),h=b.$element.prop("selectedIndex"),i=!0;if(b.multiple&&1!==b.options.maxOptions&&c.stopPropagation(),c.preventDefault(),!b.isDisabled()&&!d.parent().hasClass("disabled")){var j=b.$element.find("option"),k=j.eq(f),l=k.prop("selected"),m=k.parent("optgroup"),n=b.options.maxOptions,o=m.data("maxOptions")||!1;if(b.multiple){if(k.prop("selected",!l),b.setSelected(f,!l),d.blur(),n!==!1||o!==!1){var p=n');t[2]&&(u=u.replace("{var}",t[2][n>1?0:1]),v=v.replace("{var}",t[2][o>1?0:1])),k.prop("selected",!1),b.$menu.append(w),n&&p&&(w.append(a(""+u+"
")),i=!1,b.$element.trigger("maxReached.bs.select")),o&&q&&(w.append(a(""+v+"
")),i=!1,b.$element.trigger("maxReachedGrp.bs.select")),setTimeout(function(){b.setSelected(f,!1)},10),w.delay(750).fadeOut(300,function(){a(this).remove()})}}}else j.prop("selected",!1),k.prop("selected",!0),b.$menuInner.find(".selected").removeClass("selected").find("a").attr("aria-selected",!1),b.setSelected(f,!0);!b.multiple||b.multiple&&1===b.options.maxOptions?b.$button.focus():b.options.liveSearch&&b.$searchbox.focus(),i&&(g!=b.$element.val()&&b.multiple||h!=b.$element.prop("selectedIndex")&&!b.multiple)&&(e=[f,k.prop("selected"),l],b.$element.triggerNative("change"))}}),this.$menu.on("click","li.disabled a, .popover-title, .popover-title :not(.close)",function(c){c.currentTarget==this&&(c.preventDefault(),c.stopPropagation(),b.options.liveSearch&&!a(c.target).hasClass("close")?b.$searchbox.focus():b.$button.focus())}),this.$menuInner.on("click",".divider, .dropdown-header",function(a){a.preventDefault(),a.stopPropagation(),b.options.liveSearch?b.$searchbox.focus():b.$button.focus()}),this.$menu.on("click",".popover-title .close",function(){b.$button.click()}),this.$searchbox.on("click",function(a){a.stopPropagation()}),this.$menu.on("click",".actions-btn",function(c){b.options.liveSearch?b.$searchbox.focus():b.$button.focus(),c.preventDefault(),c.stopPropagation(),a(this).hasClass("bs-select-all")?b.selectAll():b.deselectAll()}),this.$element.change(function(){b.render(!1),b.$element.trigger("changed.bs.select",e),e=null})},liveSearchListener:function(){var c=this,d=a(' ');this.$button.on("click.dropdown.data-api",function(){c.$menuInner.find(".active").removeClass("active"),c.$searchbox.val()&&(c.$searchbox.val(""),c.$lis.not(".is-hidden").removeClass("hidden"),d.parent().length&&d.remove()),c.multiple||c.$menuInner.find(".selected").addClass("active"),setTimeout(function(){c.$searchbox.focus()},10)}),this.$searchbox.on("click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api",function(a){a.stopPropagation()}),this.$searchbox.on("input propertychange",function(){if(c.$lis.not(".is-hidden").removeClass("hidden"),c.$lis.filter(".active").removeClass("active"),d.remove(),c.$searchbox.val()){var e,f=c.$lis.not(".is-hidden, .divider, .dropdown-header");if(e=c.options.liveSearchNormalize?f.not(":a"+c._searchStyle()+'("'+b(c.$searchbox.val())+'")'):f.not(":"+c._searchStyle()+'("'+c.$searchbox.val()+'")'),e.length===f.length)d.html(c.options.noneResultsText.replace("{0}",'"'+j(c.$searchbox.val())+'"')),c.$menuInner.append(d),c.$lis.addClass("hidden");else{e.addClass("hidden");var g,h=c.$lis.not(".hidden");h.each(function(b){var c=a(this);c.hasClass("divider")?void 0===g?c.addClass("hidden"):(g&&g.addClass("hidden"),g=c):c.hasClass("dropdown-header")&&h.eq(b+1).data("optgroup")!==c.data("optgroup")?c.addClass("hidden"):g=null}),g&&g.addClass("hidden"),f.not(".hidden").first().addClass("active"),c.$menuInner.scrollTop(0)}}})},_searchStyle:function(){var a={begins:"ibegins",startsWith:"ibegins"};return a[this.options.liveSearchStyle]||"icontains"},val:function(a){return"undefined"!=typeof a?(this.$element.val(a),this.render(),this.$element):this.$element.val()},changeAll:function(b){if(this.multiple){"undefined"==typeof b&&(b=!0),this.findLis();var c=this.$element.find("option"),d=this.$lis.not(".divider, .dropdown-header, .disabled, .hidden"),e=d.length,f=[];if(b){if(d.filter(".selected").length===d.length)return}else if(0===d.filter(".selected").length)return;d.toggleClass("selected",b);for(var g=0;g=48&&b.keyCode<=57||b.keyCode>=96&&b.keyCode<=105||b.keyCode>=65&&b.keyCode<=90))return i.options.container?i.$button.trigger("click"):(i.setSize(),i.$menu.parent().addClass("open"),f=!0),void i.$searchbox.focus();if(i.options.liveSearch&&/(^9$|27)/.test(b.keyCode.toString(10))&&f&&(b.preventDefault(),b.stopPropagation(),i.$menuInner.click(),i.$button.focus()),/(38|40)/.test(b.keyCode.toString(10))){if(c=i.$lis.filter(j),!c.length)return;d=i.options.liveSearch?c.index(c.filter(".active")):c.index(c.find("a").filter(":focus").parent()),e=i.$menuInner.data("prevIndex"),38==b.keyCode?(!i.options.liveSearch&&d!=e||d==-1||d--,d<0&&(d+=c.length)):40==b.keyCode&&((i.options.liveSearch||d==e)&&d++,d%=c.length),i.$menuInner.data("prevIndex",d),i.options.liveSearch?(b.preventDefault(),g.hasClass("dropdown-toggle")||(c.removeClass("active").eq(d).addClass("active").children("a").focus(),g.focus())):c.eq(d).children("a").focus()}else if(!g.is("input")){var l,m,n=[];c=i.$lis.filter(j),c.each(function(c){a.trim(a(this).children("a").text().toLowerCase()).substring(0,1)==k[b.keyCode]&&n.push(c)}),l=a(document).data("keycount"),l++,a(document).data("keycount",l),m=a.trim(a(":focus").text().toLowerCase()).substring(0,1),m!=k[b.keyCode]?(l=1,a(document).data("keycount",l)):l>=n.length&&(a(document).data("keycount",0),l>n.length&&(l=1)),c.eq(n[l-1]).children("a").focus()}if((/(13|32)/.test(b.keyCode.toString(10))||/(^9$)/.test(b.keyCode.toString(10))&&i.options.selectOnTab)&&f){if(/(32)/.test(b.keyCode.toString(10))||b.preventDefault(),i.options.liveSearch)/(32)/.test(b.keyCode.toString(10))||(i.$menuInner.find(".active a").click(),g.focus());else{var o=a(":focus");o.click(),o.focus(),b.preventDefault(),a(document).data("spaceSelect",!0)}a(document).data("keycount",0)}(/(^9$|27)/.test(b.keyCode.toString(10))&&f&&(i.multiple||i.options.liveSearch)||/(27)/.test(b.keyCode.toString(10))&&!f)&&(i.$menu.parent().removeClass("open"),i.options.container&&i.$newElement.removeClass("open"),i.$button.focus())},mobile:function(){this.$element.addClass("mobile-device")},refresh:function(){this.$lis=null,this.liObj={},this.reloadLi(),this.render(),this.checkDisabled(),this.liHeight(!0),this.setStyle(),
8 | this.setWidth(),this.$lis&&this.$searchbox.trigger("propertychange"),this.$element.trigger("refreshed.bs.select")},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(".bs.select").removeData("selectpicker").removeClass("bs-select-hidden selectpicker")}};var m=a.fn.selectpicker;a.fn.selectpicker=c,a.fn.selectpicker.Constructor=l,a.fn.selectpicker.noConflict=function(){return a.fn.selectpicker=m,this},a(document).data("keycount",0).on("keydown.bs.select",'.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input',l.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input',function(a){a.stopPropagation()}),a(window).on("load.bs.select.data-api",function(){a(".selectpicker").each(function(){var b=a(this);c.call(b,b.data())})})}(a)});
9 | //# sourceMappingURL=bootstrap-select.js.map
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-brands-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-brands-400.eot
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-brands-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-brands-400.ttf
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-brands-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-brands-400.woff
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-brands-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-brands-400.woff2
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-regular-400.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-regular-400.eot
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-regular-400.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-regular-400.woff
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-regular-400.woff2
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-solid-900.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-solid-900.eot
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-solid-900.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-solid-900.woff
--------------------------------------------------------------------------------
/ui/src/assets/webfonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/assets/webfonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/ui/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/ui/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/ui/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nraboy/open-ledger-micro/1f28a56794b0e6c6db08aed46e9ce1963dd4a395/ui/src/favicon.ico
--------------------------------------------------------------------------------
/ui/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Open Ledger Micro
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/ui/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/ui/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Required to support Web Animations `@angular/platform-browser/animations`.
51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
52 | **/
53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
54 |
55 | /**
56 | * By default, zone.js will patch all possible macroTask and DomEvents
57 | * user can disable parts of macroTask/DomEvents patch by setting following flags
58 | */
59 |
60 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
61 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
62 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
63 |
64 | /*
65 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
66 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
67 | */
68 | // (window as any).__Zone_enable_cross_context_check = true;
69 |
70 | /***************************************************************************************************
71 | * Zone JS is required by default for Angular itself.
72 | */
73 | import 'zone.js/dist/zone'; // Included with Angular CLI.
74 |
75 |
76 |
77 | /***************************************************************************************************
78 | * APPLICATION IMPORTS
79 | */
80 |
--------------------------------------------------------------------------------
/ui/src/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 50px;
3 | margin-bottom: 0px;
4 | background-color: #293036;
5 | }
6 |
7 | .navbar-default {
8 | background-color: #21262b;
9 | border-color: #FF8C00;
10 | }
11 |
12 | .navbar-default .navbar-brand {
13 | color: #FFFFFF;
14 | }
15 |
16 | .navbar-default .navbar-brand:focus, .navbar-default .navbar-brand:hover {
17 | color: #FFFFFF;
18 | }
19 |
20 | .navbar-default .navbar-nav>li>a {
21 | color: #FFFFFF;
22 | }
23 |
24 | .navbar-default .navbar-nav>li>a:focus, .navbar-default .navbar-nav>li>a:hover {
25 | color: #FFFFFF;
26 | }
27 |
28 | .navbar-fixed-top {
29 | border-width: 0 0 2px;
30 | }
31 |
32 | .form-group-lg .form-control {
33 | border-radius: 0px;
34 | }
35 |
36 | .list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover {
37 | background-color: #FF8C00;
38 | }
39 |
40 | .panel {
41 | border-radius: 0px;
42 | border: none;
43 | }
44 |
45 | thead {
46 | background-color: #FD8B25;
47 | }
48 |
49 | .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child, .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child, .panel>.table:first-child>tbody:first-child>tr:first-child td:first-child, .panel>.table:first-child>tbody:first-child>tr:first-child th:first-child, .panel>.table:first-child>thead:first-child>tr:first-child td:first-child, .panel>.table:first-child>thead:first-child>tr:first-child th:first-child {
50 | border-radius: 0px;
51 | }
52 |
53 | .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child, .panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child, .panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child, .panel>.table:first-child>tbody:first-child>tr:first-child td:last-child, .panel>.table:first-child>tbody:first-child>tr:first-child th:last-child, .panel>.table:first-child>thead:first-child>tr:first-child td:last-child, .panel>.table:first-child>thead:first-child>tr:first-child th:last-child {
54 | border-radius: 0px;
55 | }
56 |
57 | .alert-success {
58 | word-wrap: break-word;
59 | text-align: left;
60 | border-radius: 0px;
61 | }
62 |
63 | #footer {
64 | bottom: 0;
65 | width: 100%;
66 | position: absolute;
67 | height: 40px;
68 | background-color: #21262b;
69 | color: #FFFFFF;
70 | }
71 |
72 | .footer-block {
73 | margin: 10px 0;
74 | }
75 |
76 | #footer a {
77 | color: #FFFFFF;
78 | }
79 |
80 | #vertical-layout {
81 | display: flex;
82 | min-height: 100%;
83 | min-height: 100vh;
84 | align-items: center;
85 | margin-top: -50px;
86 | }
87 |
88 | #content-box {
89 | background-color: #FFFFFF;
90 | border: 1px solid #21262b;
91 | padding: 30px;
92 | margin-top: 10px;
93 | }
94 |
95 | #content-box-title {
96 | padding: 0;
97 | margin: 0;
98 | }
99 |
100 | .no-radius {
101 | border-radius: 0px;
102 | }
103 |
104 | .red-strong {
105 | color: red;
106 | font-weight: bold;
107 | }
108 |
--------------------------------------------------------------------------------
/ui/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/ui/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "./",
6 | "module": "es2015",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/ui/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "types": [
8 | "jasmine",
9 | "node"
10 | ]
11 | },
12 | "files": [
13 | "test.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/ui/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2017",
16 | "dom"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ui/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs",
22 | "rxjs/Rx"
23 | ],
24 | "import-spacing": true,
25 | "indent": [
26 | true,
27 | "spaces"
28 | ],
29 | "interface-over-type-literal": true,
30 | "label-position": true,
31 | "max-line-length": [
32 | true,
33 | 140
34 | ],
35 | "member-access": false,
36 | "member-ordering": [
37 | true,
38 | {
39 | "order": [
40 | "static-field",
41 | "instance-field",
42 | "static-method",
43 | "instance-method"
44 | ]
45 | }
46 | ],
47 | "no-arg": true,
48 | "no-bitwise": true,
49 | "no-console": [
50 | true,
51 | "debug",
52 | "info",
53 | "time",
54 | "timeEnd",
55 | "trace"
56 | ],
57 | "no-construct": true,
58 | "no-debugger": true,
59 | "no-duplicate-super": true,
60 | "no-empty": false,
61 | "no-empty-interface": true,
62 | "no-eval": true,
63 | "no-inferrable-types": [
64 | true,
65 | "ignore-params"
66 | ],
67 | "no-misused-new": true,
68 | "no-non-null-assertion": true,
69 | "no-shadowed-variable": true,
70 | "no-string-literal": false,
71 | "no-string-throw": true,
72 | "no-switch-case-fall-through": true,
73 | "no-trailing-whitespace": true,
74 | "no-unnecessary-initializer": true,
75 | "no-unused-expression": true,
76 | "no-use-before-declare": true,
77 | "no-var-keyword": true,
78 | "object-literal-sort-keys": false,
79 | "one-line": [
80 | true,
81 | "check-open-brace",
82 | "check-catch",
83 | "check-else",
84 | "check-whitespace"
85 | ],
86 | "prefer-const": true,
87 | "quotemark": [
88 | true,
89 | "single"
90 | ],
91 | "radix": true,
92 | "semicolon": [
93 | true,
94 | "always"
95 | ],
96 | "triple-equals": [
97 | true,
98 | "allow-null-check"
99 | ],
100 | "typedef-whitespace": [
101 | true,
102 | {
103 | "call-signature": "nospace",
104 | "index-signature": "nospace",
105 | "parameter": "nospace",
106 | "property-declaration": "nospace",
107 | "variable-declaration": "nospace"
108 | }
109 | ],
110 | "unified-signatures": true,
111 | "variable-name": false,
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ],
120 | "directive-selector": [
121 | true,
122 | "attribute",
123 | "app",
124 | "camelCase"
125 | ],
126 | "component-selector": [
127 | true,
128 | "element",
129 | "app",
130 | "kebab-case"
131 | ],
132 | "no-output-on-prefix": true,
133 | "use-input-property-decorator": true,
134 | "use-output-property-decorator": true,
135 | "use-host-property-decorator": true,
136 | "no-input-rename": true,
137 | "no-output-rename": true,
138 | "use-life-cycle-interface": true,
139 | "use-pipe-transform-interface": true,
140 | "component-class-suffix": true,
141 | "directive-class-suffix": true
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/wallet.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/aes"
5 | "crypto/cipher"
6 | "crypto/md5"
7 | "crypto/rand"
8 | "encoding/hex"
9 | "encoding/json"
10 | "errors"
11 | "io"
12 | "io/ioutil"
13 | "os"
14 | )
15 |
16 | type Wallet struct {
17 | Coins []Coin `json:"coins"`
18 | }
19 |
20 | func (wallet Wallet) CreateHash(key string) string {
21 | hasher := md5.New()
22 | hasher.Write([]byte(key))
23 | return hex.EncodeToString(hasher.Sum(nil))
24 | }
25 |
26 | func (wallet Wallet) Encrypt(passphrase string) ([]byte, error) {
27 | block, err := aes.NewCipher([]byte(wallet.CreateHash(passphrase)))
28 | if err != nil {
29 | return nil, err
30 | }
31 | gcm, err := cipher.NewGCM(block)
32 | if err != nil {
33 | return nil, err
34 | }
35 | nonce := make([]byte, gcm.NonceSize())
36 | io.ReadFull(rand.Reader, nonce)
37 | data, err := json.Marshal(wallet)
38 | if err != nil {
39 | return nil, err
40 | }
41 | ciphertext := gcm.Seal(nonce, nonce, data, nil)
42 | return ciphertext, nil
43 | }
44 |
45 | func (wallet *Wallet) Decrypt(data []byte, passphrase string) error {
46 | block, err := aes.NewCipher([]byte(wallet.CreateHash(passphrase)))
47 | if err != nil {
48 | return err
49 | }
50 | gcm, err := cipher.NewGCM(block)
51 | if err != nil {
52 | return err
53 | }
54 | nonceSize := gcm.NonceSize()
55 | nonce, ciphertext := data[:nonceSize], data[nonceSize:]
56 | plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
57 | if err != nil {
58 | return err
59 | }
60 | json.Unmarshal(plaintext, wallet)
61 | return nil
62 | }
63 |
64 | func (wallet Wallet) EncryptFile(passphrase string) error {
65 | file, err := os.Create("wallet.dat")
66 | if err != nil {
67 | return err
68 | }
69 | defer file.Close()
70 | data, err := wallet.Encrypt(passphrase)
71 | if err != nil {
72 | return err
73 | }
74 | _, err = file.Write(data)
75 | return err
76 | }
77 |
78 | func (wallet *Wallet) DecryptFile(passphrase string) error {
79 | ciphertext, err := ioutil.ReadFile("wallet.dat")
80 | if err != nil {
81 | return err
82 | }
83 | err = wallet.Decrypt(ciphertext, passphrase)
84 | if err != nil {
85 | return err
86 | }
87 | return nil
88 | }
89 |
90 | func (wallet Wallet) Create(key string) error {
91 | err := wallet.EncryptFile(key)
92 | return err
93 | }
94 |
95 | func (wallet Wallet) Destroy() error {
96 | err := os.Remove("wallet.dat")
97 | return err
98 | }
99 |
100 | func (wallet Wallet) Import(coin Coin, passphrase string) error {
101 | if coin == (Coin{}) {
102 | return errors.New("The coin must be valid")
103 | }
104 | wallet.Coins = append(wallet.Coins, coin)
105 | wallet.EncryptFile(passphrase)
106 | return nil
107 | }
108 |
109 | func (wallet *Wallet) Dump(passphrase string) error {
110 | err := wallet.DecryptFile(passphrase)
111 | if err != nil {
112 | return errors.New(`{ "message": "The password is not correct" }`)
113 | }
114 | return nil
115 | }
116 |
117 | func (wallet *Wallet) GetAddresses(passphrase string) error {
118 | err := wallet.DecryptFile(passphrase)
119 | if err != nil {
120 | return errors.New(`{ "message": "The password is not correct" }`)
121 | }
122 | for index, _ := range wallet.Coins {
123 | wallet.Coins[index].WIF = ""
124 | }
125 | return nil
126 | }
127 |
128 | func (wallet *Wallet) Authenticate(passphrase string) bool {
129 | err := wallet.DecryptFile(passphrase)
130 | if err != nil {
131 | return false
132 | }
133 | return true
134 | }
135 |
--------------------------------------------------------------------------------
/wallet_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 |
7 | "github.com/btcsuite/btcutil"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestWalletCipher(t *testing.T) {
12 | var wallet Wallet
13 | passphrase := "1234"
14 | defer wallet.Destroy()
15 | wallet.Create(passphrase)
16 | err := wallet.EncryptFile(passphrase)
17 | assert.Nil(t, err, "The `err` should be `nil`")
18 | err = wallet.DecryptFile(passphrase)
19 | assert.Nil(t, err, "The `err` should be `nil`")
20 | err = wallet.DecryptFile("testing")
21 | assert.NotNil(t, err.Error(), "The `err` should not be `nil`")
22 | }
23 |
24 | func TestWalletImport(t *testing.T) {
25 | var wallet Wallet
26 | passphrase := "1234"
27 | defer wallet.Destroy()
28 | wallet.Create(passphrase)
29 | var coin Coin
30 | err := wallet.Import(coin, passphrase)
31 | assert.NotNil(t, err.Error(), "The `err` should not be `nil`")
32 | coin.Generate(network["btc"])
33 | err = wallet.Import(coin, passphrase)
34 | assert.Nil(t, err, "The `err` should not be `nil`")
35 | }
36 |
37 | func TestWalletAddresses(t *testing.T) {
38 | var wallet Wallet
39 | passphrase := "1234"
40 | defer wallet.Destroy()
41 | wallet.Create(passphrase)
42 | var coin Coin
43 | coin.Generate(network["btc"])
44 | wallet.Import(coin, passphrase)
45 | wallet.GetAddresses(passphrase)
46 | assert.Equal(t, wallet.Coins[0].WIF, "", "The `wif` should not be present")
47 | wallet.Dump(passphrase)
48 | }
49 |
50 | func TestAddress(t *testing.T) {
51 | decAddr, err := btcutil.DecodeAddress("1KKKK6N21XKo48zWKuQKXdvSsCf95ibHFa", network["btc"].GetNetworkParams())
52 | if err != nil {
53 | fmt.Printf("DecodeAddress: %v\n", err)
54 | }
55 | pkh, ok := decAddr.(*btcutil.AddressPubKeyHash)
56 | if !ok {
57 | fmt.Printf("invalid type: %T\n", pkh)
58 | }
59 |
60 | if !pkh.IsForNet(network["btc"].GetNetworkParams()) {
61 | fmt.Printf("address not for %s\n", network["btc"].GetNetworkParams().Name)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------