diff --git a/.gitignore b/.gitignore index e53e461dc6..45ee901e50 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ build/_vendor/pkg # travis profile.tmp profile.cov + +# dashboard +/go-ethereum/dashboard/bundler/node_modules/ \ No newline at end of file diff --git a/dashboard/README.md b/dashboard/README.md new file mode 100644 index 0000000000..fccae100d4 --- /dev/null +++ b/dashboard/README.md @@ -0,0 +1,32 @@ +## Go Ethereum Dashboard +### Description + +The GED is a data visualizer integrated into geth, intended to collect and visualize useful information of an Ethereum node. +The GED consists of two parts: +* The server listens to connections, collects data with a given refresh rate, and updates the dashboards through the opened connections. +* The client waits for update messages, updates the content and tries to reconnect on connection loss. + +The client's UI is maintained by [Inferno][Inferno], a sympathetic React-like JavaScript library. +In order to create the Inferno's virtual DOM using JSX syntax, there is need to use the provided babel plugin. +[Webpack][Webpack] is used for bundling the resources in order to gain cost efficiency and maintainability. +[Node.js][Node.js] is used for installing the necessary dependencies. + +### Installation steps + +_Server_ + +1. `cd .../go-ethereum/` +1. `go generate ./dashboard && go install -v ./cmd/geth` +1. run the server with `geth --rinkeby --dashboard --vmodule=dashboard=5 --metrics console` +1. optionally use `--dashboard.assets=` to set the assets' path => useful for debugging +1. enter `localhost:8080` (or change the configuration) + +_Module bundler_ + +1. `cd .../go-ethereum/dashboard/bundler/` +1. `npm install` +1. `./node_modules/.bin/webpack` // check out `webpack.config.js` + +[Inferno]: https://infernojs.org/ +[Webpack]: https://webpack.github.io/ +[Node.js]: https://nodejs.org/en/ \ No newline at end of file diff --git a/dashboard/assets.go b/dashboard/assets.go index b10ac86507..e11a9f1c86 100644 --- a/dashboard/assets.go +++ b/dashboard/assets.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. // sources: -// assets/components/dashboard.js // assets/dashboard.html +// assets/js/bundle.js // DO NOT EDIT! package dashboard @@ -69,27 +69,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _componentsDashboardJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x7b\x6f\x1b\x37\x12\xff\xdf\x80\xbf\xc3\x9c\x7a\x68\x65\x54\xd2\xca\xf6\xa5\x0d\xf4\x02\x12\x27\x69\x7d\x88\x93\x22\x36\x50\x1c\x8c\xc0\xa5\x76\x47\xbb\x84\xb9\xe4\x82\xe4\xca\x36\x02\x7d\xf7\x03\xb9\x0f\xed\x53\x5a\x3b\x76\x51\x01\x41\x24\x92\xf3\xe3\x0c\x39\x6f\xfa\x2c\x20\x52\x8f\x3c\x5c\x91\x98\x69\x35\xf2\x99\x58\x12\x36\x62\xe8\x23\xf7\x60\x0e\xdf\x0e\x0f\x00\x00\x90\x93\x25\x43\x6f\x02\x2b\xc2\x14\x1e\x1e\x6c\xa6\x87\x07\x87\x07\xae\xe0\x4a\xc3\x99\x08\x23\xc1\x91\x6b\x98\xc3\x17\x24\xae\x1e\xe5\x23\xd3\xc3\x03\xc7\x81\xfa\xb2\x73\xbe\x42\xc9\x45\x69\xe1\xe1\x81\xcb\x88\x52\x70\xc6\x90\xc8\x15\xbd\x07\xbc\xd7\xc8\x3d\x55\x20\x4c\x99\x91\xc8\x3d\x94\xfd\xa3\xec\x77\x32\xa6\x63\xc9\xa1\xbf\x1d\x31\x9f\x99\x47\xd7\x60\x61\x3f\x91\x10\xe7\x3d\x37\xc5\xee\x39\x8b\xed\xc2\xa3\x69\xf2\x7d\x73\x78\xb0\xd9\xb2\x71\x81\x3c\x3e\xd7\x18\x3e\x0b\x1b\x8c\x2e\x66\x64\x31\xa3\x05\x5e\xbe\xfd\xb5\x22\xf0\xef\x6f\x3a\xa0\x6a\x14\x49\x11\xa9\x51\x3e\xb7\xf9\x0b\x36\xce\xa2\x38\xa5\xf1\x5e\x6f\xaa\xc2\xac\x08\xac\xc8\xd0\x0d\x70\x2d\x05\x1f\x7a\xe2\x8e\xf7\x9c\xc5\xcc\x21\x8b\xf2\xe6\x96\x81\x98\x15\x29\x39\x59\x83\x1b\x50\xe6\xdd\x84\xc8\xe3\x5e\x03\x81\xf9\x94\x78\x33\xab\x25\xf2\x4d\x03\xb6\x13\xb3\x0a\xc2\xcc\x61\xb4\xd3\x01\x9f\x19\xd8\xe7\x3b\x61\x08\x24\xae\xe6\x45\xbe\xcd\xc0\xa6\xe1\x28\x1d\xb2\xe8\xcc\xe4\x25\x32\x74\xf5\xb3\x70\x59\xbf\x06\x45\x3d\x1c\xb6\xdd\xc2\x2c\x57\xc2\xd2\xb5\x0f\x03\x11\x62\x0f\x8c\x24\xf3\xde\xef\xe6\x7b\xcb\x0d\xce\xb6\x67\x6c\x4f\xa6\xe7\x11\x15\x2c\x05\x91\xde\x28\xd0\x21\xcb\x20\xde\x65\xa3\xc7\x25\xbb\xf8\x1e\xa4\x93\x46\xa4\x99\x93\x09\xf4\x08\x61\xd1\xa3\x3a\xc3\xff\x84\xfa\x4e\xc8\x5b\xca\xfd\xef\x14\x79\x0b\xd4\x2c\xf3\x93\x38\xf5\x50\xdd\x6a\x11\x65\x7b\x5c\xdd\x47\x42\xb0\xef\x64\x34\x01\x79\x46\x26\xb5\x71\xe4\x19\xfa\x47\xe1\xab\x7f\x1a\x83\x4b\x22\x87\xae\x09\x4a\x43\x91\xed\x71\xf9\xa0\x34\x86\xcd\x5b\xb4\xa0\xb8\x4c\xf0\x5c\xcc\xb7\x4c\xb8\xb7\x6e\x40\x28\xff\x4e\x61\x0b\x40\x8f\x92\xb7\xe2\x23\x9b\x7d\xcd\x25\xf5\xf0\x2d\x91\x2f\x12\xf7\x04\x1b\x86\xde\xf0\x14\x18\xae\xf4\x8d\xdb\xac\x95\x55\xa2\x6c\x2d\x28\x57\x0a\xc6\x86\x6b\x8a\x77\xad\xe7\x57\xa1\xe5\x64\xbd\x24\x12\x38\x59\xdf\x68\xaa\x8d\xc2\x29\xfd\xc0\x70\xfe\xed\xdb\x52\x48\x0f\xe5\x04\xc6\x9b\x4d\x0b\x96\xc5\x23\x2d\xf7\x50\xd8\x43\x51\x8d\x29\x7a\x39\xb4\xa6\x91\x31\x22\x77\x3d\x67\x01\x33\x15\x11\xbe\xf8\x4d\xc0\x7b\x1d\xa0\xc4\x38\x84\xdc\x4b\xcd\x1c\x3b\xd7\x1c\x33\x93\x7b\xf3\xe8\xba\x6d\x2e\x4b\x54\x5a\x7d\xa6\x39\x13\xea\x19\x46\x3d\x34\x5a\x6d\xdd\x7c\x91\xcf\x90\x50\x6e\x43\xf0\x8d\x59\x02\x01\xf5\x3c\xe4\xc3\x48\x52\xae\x21\x9f\x6b\x3b\xf2\xa6\x63\x4f\xb0\xd0\xd5\x54\xb4\xaa\x7a\x4e\xbb\x8d\x6d\x6d\x12\xec\x3d\x82\x96\xb9\xa6\xf1\xea\x58\xb3\x0d\x5c\x89\xe8\x13\x59\x53\x9f\x18\x09\x5e\xc2\x12\xb4\x88\x6e\x38\x59\x77\xd1\x7f\xa3\xbc\xbb\x2e\x60\x66\xd3\xa8\xed\xfa\x1e\x48\xc1\x12\xba\x54\x80\xc7\x5c\x9d\x01\xd3\xc2\xf7\x59\x6b\x34\xcf\x29\x89\xd5\x2a\x7b\xd7\xdd\x28\x2c\x55\x83\x85\x2c\x89\x54\xad\x21\x3f\x27\x6c\xb5\x0e\xd8\xab\x1e\x9c\x3c\xaf\x7a\xec\x4e\xc8\x6d\x9d\x21\x63\x57\x0b\xd9\xb7\xc9\x5e\x49\x3f\x54\x1c\x61\x36\x3e\xdd\x0e\xdb\xd4\x50\x69\xa2\x71\x5b\xed\x64\x1f\x1b\x86\x26\xc0\x63\xc6\x06\xdb\x99\x4d\xce\x59\xb6\x6d\xca\xc7\x3b\xea\x5d\x88\x98\xeb\xb2\x5e\x6e\x37\x18\x59\x3c\x98\x03\xc7\x3b\xb0\x75\x57\xdf\x4e\x7a\x44\x93\x41\x75\x6f\xfd\x10\xe1\x04\x7e\x62\x94\xe3\x4f\x83\xf2\x94\x59\x3f\x81\x52\x76\x4e\xa4\x7e\x67\x50\x0a\x5c\x16\x4e\xb0\xdd\x68\xe8\xaa\x5f\xe3\xef\x5f\xf3\xb9\x15\xf9\xa8\xc6\x52\x65\xe5\x28\x8e\x3c\xa2\xb1\x5f\x3c\xce\x7c\x37\xe8\x6a\x92\xcd\x25\x50\x07\xfb\xbc\xbf\x89\x08\xc7\xf6\x04\xab\xb6\x3c\x8d\x15\x3b\x94\x39\x38\xa9\xd7\x0a\x30\x73\x82\x93\x5d\x44\xf5\xa4\x3e\x89\x7e\x43\x49\xfd\x40\x83\x65\xf2\x46\x0b\xc1\x96\xe2\x7e\xaf\x6d\x27\x75\x4c\x39\x74\x33\x12\x29\x1c\x32\xca\x6f\x9b\x03\x5d\x56\x02\xc6\x51\x6b\x01\x58\xda\xa3\x5c\xf8\xb4\xb0\x51\xdc\xc6\x93\x22\xb2\xf5\x65\x17\x3f\x93\x45\xee\x1f\x7a\x4d\x10\xc3\xd4\x63\x59\x25\x4e\x7f\x14\x36\x48\x5d\xe8\x32\xd6\x5a\xf0\x1e\x10\x49\xc9\x10\xef\x23\xc2\x3d\xf4\x8c\xb8\x4c\xb5\x04\xfb\x3b\x89\xdc\x0d\x3a\xc9\x0f\xf5\x4b\xcb\xb9\x4b\x42\x74\xc2\xc4\xbe\xe0\x5b\x39\xb0\x45\x41\xf2\xc5\x25\x6a\x4d\xb9\xaf\xe0\xb8\x1b\x3f\xd0\xed\x5e\xf6\x6e\x77\xf2\xec\xdb\x35\x94\xf7\x4f\x40\x6a\xd0\x6b\x26\x76\x2b\xb5\x99\x7f\x0e\x7d\xde\x23\xc0\xfe\x34\x6e\x57\x84\xab\xf9\x18\x57\x70\x8d\x5c\xef\xf4\x32\x2e\xe1\x6b\xa2\xc0\xf6\x2a\xfa\xc6\x0e\x8e\x60\xbe\x48\xdb\x2d\xe6\x27\xcc\xad\x75\x6c\x36\x8f\x65\xe9\xe9\x81\xf5\x8b\xb8\x7b\x89\x6c\x4b\x8a\xc6\x8a\x61\x66\xe2\x78\xb3\x68\xf5\xa2\xe5\x17\x30\x5f\x54\x98\x7e\xb9\x57\xc3\xe3\x93\x5e\x33\x71\x52\xa4\x5d\x60\x28\xe4\x03\xc4\x8a\xf8\x08\xca\x96\x8e\x4e\x68\xc7\x1c\xca\x63\x85\x2d\xc4\x79\x10\x2d\x47\x24\x33\xaa\x46\x09\x7d\x43\xf7\xab\xb1\x0c\x7c\x51\xf1\xce\xf9\x52\xc4\xdc\x03\x2d\xc9\x6a\x45\x5d\x88\x4e\x22\x27\x1d\xbb\x4a\x86\x9e\x22\x60\x8a\xd6\x41\xc2\x6e\x0a\xf5\x07\xf1\xf1\x2c\xb1\x85\x17\x51\x2c\x13\x5a\x6d\x21\x9b\x39\xeb\x96\xe2\xbe\x4a\xd8\x35\x5b\x88\x88\x8f\xc3\xfd\x09\x43\xb5\xba\x30\x04\x37\xa6\x70\xde\x1b\xe8\x83\xd3\xe6\x9a\x14\x66\x2a\x24\x8c\x2d\x2e\x35\xd1\x54\x69\xea\xaa\x99\x93\x8c\xcc\x9c\xe0\xf4\x99\x0b\x35\xe8\xe4\x00\x8d\x73\x48\xd4\xa4\x41\x73\xda\x9d\xd4\x2e\xdc\x27\x79\x2a\xfb\x8c\x70\xf1\xfe\xe2\xf3\x97\xff\xdd\x5c\xbe\xb9\xf8\xe3\xe3\xfb\x9b\x8f\xe7\x17\xe7\x57\x30\x87\x93\xf1\x78\x0a\x8e\x03\x17\xe4\x9e\x86\x71\x08\x3c\x0e\x97\x28\x41\xac\x20\xb1\x5c\xeb\x50\x41\x91\x30\x62\xa8\x32\xa8\xab\x2f\x6f\x3e\x7c\x38\x3f\xeb\x8e\x95\x99\x5c\x19\x2c\x53\xf9\xed\x15\xfe\xcd\x15\x8a\x9a\x54\x87\xcd\x27\x11\xbc\x71\xca\x7c\x18\x59\x22\x53\x13\xb8\xfe\x3a\x68\x5e\x60\x84\x54\x68\xc0\xaf\x5b\x20\x72\x98\x09\xf4\x9a\xfc\x6c\x0b\xb0\xf9\x2c\x89\x7b\xeb\x4b\xe3\xb3\xce\x04\x13\x72\x02\x3d\xe9\x2f\x49\xff\xf4\xf5\x00\x8e\x5f\xbf\x1a\xc0\xf1\xab\xff\x0c\x60\x3c\x3a\x3d\x3e\xda\x89\x62\x7b\x49\x3b\x11\x7e\xdd\x09\x10\x09\xca\xf5\xdb\xe7\x41\xe9\x26\x51\x07\xa4\xdf\xc5\x1a\x65\x1d\xee\x87\xd5\x6a\xd5\x91\xb6\x2e\xd0\xc9\xc9\x78\x90\xfd\xdb\x7d\xa6\x85\x23\xf9\x93\x7a\x3a\x98\xc0\xf1\x8e\xd5\x49\x29\x7a\xfd\xb5\x79\xc5\xa6\x61\x7c\xd3\x80\x96\xda\xd5\xdf\xa5\xab\x0d\x11\xf3\x09\xaa\x3a\x80\xd7\xe6\x6e\xc7\xbf\x58\x45\x7d\xb4\x9e\x96\xc8\x7f\x1d\x3f\x4d\x4d\x1f\x0f\xd2\x45\x98\x2e\x40\x2f\xa1\xa4\xc7\xaf\x8e\x07\xc7\xaf\x7f\x1d\x9c\x8c\x5f\xfd\xc3\x94\xb4\xf8\x7b\x53\xec\xab\x24\x5f\x93\x3e\x88\xed\xe8\x28\x98\x43\x3f\x54\x7e\x92\xd2\x6f\xa9\x18\xea\x2c\x14\xcd\x6b\x8d\x94\x2c\xbf\x9c\x96\xd7\x67\xe1\xa6\x89\x20\x9d\x9b\x16\xdb\x2d\x8e\x03\x1f\x28\x63\xa0\x03\x84\xbc\x6f\x0e\x77\x54\x07\x10\x11\xa5\xed\x39\x94\x5a\x3f\xa1\xf2\x47\x21\x6a\x49\x5d\x65\xbb\x3e\x31\xf7\x70\x45\x39\x7a\xb5\xd6\x8f\xe3\x24\xef\xf2\x40\x39\xb8\x44\x61\x75\x0f\xa2\x40\x44\xc8\xd1\x83\x25\xae\x84\xc4\x32\x75\x22\xdd\x28\x31\x63\x98\xc3\xf5\xd7\x69\xa5\xb3\x94\x48\xb3\x63\x45\x0a\x91\x19\xfa\xf5\xf8\x6b\x56\x2a\xb5\x82\xb5\xad\x2d\xaf\x4e\x2f\x06\xe6\x50\x38\x8d\xfa\x7d\x64\x4b\x23\x29\x5c\x54\x4a\xc8\x0a\x41\x3e\x3e\xad\x1d\xdc\xb9\x06\xaa\x20\x12\x4a\xd1\x25\x43\xd0\x02\x7c\xd4\x40\xb8\x30\xb9\x1f\x84\xa8\x6c\xa1\x72\x17\x50\x86\xb0\xa2\x8c\x51\xee\xdb\xe3\x25\x52\x92\x07\x05\xcb\x07\x88\x62\x15\xf4\x8f\x06\xa0\x04\x50\xae\x34\x12\xaf\xb6\x49\x14\x6b\x4b\x14\x50\xa5\x8d\x92\x69\x61\x7f\x2e\xd1\xa7\x9c\x1b\x44\x93\xbe\xe4\xa0\x65\xf2\x95\x90\xd0\x37\xc2\x51\x23\x14\x86\x23\x86\xdc\xd7\x01\x0c\xe1\x78\x0a\x14\x16\x73\x18\xc3\x8f\x3f\x36\x66\x5e\x8b\xf2\xdd\xa6\x94\x53\x18\x0e\x69\x4d\x87\x6a\x9a\x30\x8a\xb9\x0a\xe8\x4a\xf7\x43\x0c\xaf\xe9\xd7\x91\xa6\x21\x8e\x54\xbc\x54\x5a\x52\xee\x97\x46\x73\x8e\x5e\x1d\x1d\x4d\x5b\x03\xc8\xb3\xe3\xb6\x68\x5d\x75\x83\x35\x61\x31\xee\xe2\xab\x15\x20\x57\x9b\x56\x98\x4d\x53\x0f\x16\xb5\x29\x0f\xb0\x5f\x3f\xe1\x1d\x09\x61\x21\x27\x4c\xfe\x6f\x76\x99\x79\x30\x4e\xbf\xd4\x57\x55\x58\xda\x54\x59\x4e\x4a\xb8\xb6\xa6\xb0\xe3\xa4\xfe\xb2\xc1\x15\x59\xff\xb8\xdb\x13\x99\xb5\x0d\x2a\x07\xf3\xf9\xbc\x49\x43\x3b\x68\x61\x72\x13\x1d\xd4\xaa\x7d\x61\x9b\x9e\xec\x87\xee\x48\xb2\xd9\xe1\x50\x47\xd6\x3d\x6c\x0f\xb0\xa6\xef\x95\x99\x1d\x3a\x5f\x11\xf8\xf9\x80\xdb\x0e\xa8\xba\x43\xa3\x09\xb4\x9e\x55\x4e\x9d\x9b\x51\x0b\xc0\x1e\xa3\xd9\x63\x35\x9d\xcc\xa6\x83\xdd\xec\x34\x9c\x92\x8d\x98\x3a\x51\x30\x1c\x31\xe1\x17\xde\x67\xf2\x82\x78\xba\x7d\xd2\x71\x05\xe7\xe8\x6a\x93\x75\x34\xa5\x1c\x0a\xe5\x1a\x65\xfa\xe4\xf4\x27\x2e\x2f\x85\x7b\x8b\xba\xdf\xbb\x53\x13\xc7\xe9\xc1\xcf\xc0\x84\x6b\x9f\x2a\x47\x81\x50\x1a\x7e\x86\x9e\x43\x22\xda\x2b\x32\xe6\x38\x70\xf5\xf9\xdd\x67\xe8\xdf\xc6\xf2\x56\x84\x54\xd1\xa3\x09\x7c\x41\x25\xd8\x1a\xed\xc9\x26\x19\xc6\x92\x72\xaf\x92\xbe\x04\x44\xa7\xb9\x4b\x01\x2e\xe1\x68\x24\x78\x16\xf8\xe6\xd0\xc7\x35\x72\x5d\x65\x3f\x83\x09\x95\x0f\x73\xf8\xef\xe5\xe7\x4f\xa3\x88\x48\x85\xc9\x6a\xab\x00\xd5\x7b\xa6\x2b\x9b\x7b\x59\x5f\xd0\xf8\x82\x05\x4d\xde\xa9\x76\xfa\xf6\x36\x03\x92\xbd\x6f\x25\x79\x9d\x4d\xea\xa6\xe5\x34\xb0\x41\x2a\xdb\xb6\xde\xde\x06\x28\xd4\x57\x34\x44\x11\xeb\xbe\x85\xcc\x6f\x6c\x00\xa7\xe3\xf1\xf8\x68\xba\xcd\x27\xa7\x8f\x79\x50\xcc\x71\xfa\x9d\xde\xf9\xba\xfe\x99\x08\xd7\x84\x72\x94\xb0\x14\xde\x43\x97\x76\x9a\xfd\x2b\x85\x9c\xac\xb5\xb9\x96\xfe\x69\x4b\x6b\xab\xa8\xf4\xec\xdf\xba\xaa\xd8\x53\x2c\xf5\xa3\x8a\x99\x71\x7b\x3f\x6a\x25\x84\x46\xf9\x88\x6e\x5e\x14\x33\x96\x3c\xe2\xed\xeb\xe6\xfd\x86\x5c\x23\x43\xc6\x08\x0c\xe1\xad\x10\x5a\x69\x49\x22\x78\xe3\x85\x94\xc3\x15\x86\x11\x23\x1a\x4d\x26\x97\x3f\xda\x04\x5a\x47\xc6\x06\x5d\x53\x00\x31\xba\x1c\xb9\x22\xec\x2d\xce\xd2\x5f\x4f\x7f\x73\x87\x6e\x4f\x1a\xad\x67\xf1\xd8\xc6\xdc\xff\x03\x00\x00\xff\xff\x72\x1b\xde\x84\x3f\x2c\x00\x00") - -func componentsDashboardJsBytes() ([]byte, error) { - return bindataRead( - _componentsDashboardJs, - "components/dashboard.js", - ) -} - -func componentsDashboardJs() (*asset, error) { - bytes, err := componentsDashboardJsBytes() - if err != nil { - return nil, err - } - - info := bindataFileInfo{name: "components/dashboard.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} - a := &asset{bytes: bytes, info: info} - return a, nil -} - -var _dashboardHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x96\xdf\x6f\x23\x27\x10\xc7\xdf\x4f\xba\xff\x81\xf0\x92\x44\x3a\xc0\x8e\xd3\xdc\xe9\xba\x6b\xb5\x4d\xd2\x2a\x0f\x55\xaa\x36\x27\xb5\x4f\x15\x86\xf1\x2e\x36\x0b\x7b\x30\x6b\xc7\xff\x7d\xb5\x3f\xec\xee\xd6\x76\xdb\xbb\x38\x7e\x31\x0c\xf0\xfd\xcc\x30\x30\x6c\x72\x76\xf7\x78\xfb\xf4\xc7\x2f\xf7\x24\xc7\xc2\x4e\xdf\xbe\x49\xea\x7f\x62\xa5\xcb\x52\x0a\x8e\x4e\xdf\xbe\x21\x84\x90\x24\x07\xa9\xbb\x76\xd3\x2f\x00\x25\x51\xb9\x0c\x11\x30\xa5\x9f\x9e\x7e\x64\x1f\xe8\xde\x78\x8e\x58\x32\xf8\x5c\x99\x55\x4a\x7f\x67\x9f\xbe\x67\xb7\xbe\x28\x25\x9a\x99\x05\x4a\x94\x77\x08\x0e\x53\xfa\x70\x9f\x82\xce\x60\x7f\xb9\x93\x05\xa4\x74\x65\x60\x5d\xfa\x80\xbd\x15\x6b\xa3\x31\x4f\x35\xac\x8c\x02\xd6\x74\xde\x11\xe3\x0c\x1a\x69\x59\x54\xd2\x42\x3a\xae\xd5\x7a\x7a\x68\xd0\xc2\xf4\x27\x4f\xee\x31\x87\x00\x55\x41\xee\x64\xcc\x67\x5e\x06\x9d\x88\x76\xb0\x37\xdb\x1a\xb7\x24\x01\x6c\x4a\x63\xee\x03\xaa\x0a\x89\x51\xde\x51\x82\x9b\x12\x52\x6a\x0a\x99\x81\x30\xca\x53\x92\x07\x98\xa7\xb4\x8e\x33\x7e\x14\x02\x3a\x71\xee\x43\x26\xe6\x72\x55\x2f\xe2\xf5\x3c\x31\xf4\xa6\xd1\x1f\x2e\x55\xda\x2d\x22\x57\xd6\x57\x7a\x6e\x65\x00\xae\x7c\x21\xe4\x42\x3e\x0b\x6b\x66\x51\xe0\xda\x20\x42\x60\x33\xef\x31\x62\x90\xa5\x98\xf0\x09\x7f\x2f\x54\x8c\x62\x67\xe3\x85\x71\x5c\xc5\x48\x3b\xdf\x71\x63\x21\xe6\x00\x48\x89\xd8\x0b\xef\x8b\xf0\x73\xef\x90\xc9\x35\x44\x5f\x80\xb8\xe6\xef\xf9\xa8\x21\xf7\xcd\xaf\x07\xcf\xc0\x21\x58\xb0\x56\x8a\x31\x9f\x74\x68\x55\x45\xf4\xc5\x71\xe8\x70\xc3\xa3\x0a\xa6\x44\x12\x83\xfa\xdf\xd4\x99\x9c\x81\x65\x11\xa5\xd3\xd2\x7a\x07\xe2\x86\x5f\xdd\xf0\x51\x6b\x6f\xb8\x8b\x48\xa7\x89\x68\xa5\xa7\x2f\x83\x05\x90\x0a\xc5\xf8\x1b\x7e\xc3\xc7\x6d\xe7\xf5\x09\x4c\x77\xfb\x77\x6a\x0a\x0b\xbe\x42\x08\xe2\x9a\x8f\xf9\xd5\xc0\x74\x08\xf7\x32\xe0\xe2\x73\x05\x61\x23\xae\xf8\x98\x5f\x77\x9d\x53\xc7\xd4\xaa\x32\xe7\xb1\xe6\x5c\xf3\xb1\x28\xa5\x5a\xca\x0c\xf4\x16\x58\x0f\xf1\xad\xf1\xd4\xf8\xdb\x5c\x06\xe4\x8b\x28\xae\xf8\x88\x8f\x76\xdd\x93\xe9\x1f\xab\x2c\x8b\x7f\x16\x96\x13\x32\xe7\xd6\xa3\x18\xf1\x0f\x7c\xb2\xdd\xc2\xda\x72\xf8\x64\x24\xa2\xf7\xf4\x24\x33\xaf\x37\xc3\x43\x73\xc6\xd8\xd6\x85\xb6\x3a\x23\x3c\xa3\x58\xc4\x67\xda\xba\xf4\x9b\x2f\x00\x73\xe3\x32\x12\xa0\xb4\x52\xd5\xad\xe6\x0e\xf7\x58\x8c\x4d\x0f\x2b\x0e\x82\xaa\x5c\xb9\xcc\x9a\x48\x8c\x9b\x43\x70\xfe\xbb\x49\x53\x09\xb5\x89\xb8\x35\x0d\x63\xf8\x4a\x5d\xa6\x7c\x51\x7a\x07\x0e\x0f\x10\xfe\x1e\x3c\x0d\xab\xbd\x99\x87\x40\xc7\xee\x6c\x43\xea\xb1\xb4\x59\x11\xa3\x53\xaa\xb7\x2f\x2a\x25\xca\xca\x18\x53\xea\xe4\x8a\x15\xba\x5e\xaa\xcd\xea\xe0\x5d\xef\xa5\xac\x4d\x4a\xeb\xee\x2e\xc6\x28\x76\xaa\xff\x75\x00\xf7\xa4\x7a\x73\xea\xdf\xaf\x75\x21\xba\x7b\xfc\x99\x07\x70\x1a\xc2\x45\xb2\xfb\x02\x10\xd3\x77\x44\x7b\x55\x15\xf5\xa6\x66\x80\xf7\x16\xea\xe6\x0f\x9b\x07\x7d\x71\xbe\xe3\x9f\x5f\x5e\x7e\xdb\xa3\x1e\xac\x60\x87\x0f\x63\xe7\xce\x20\x43\xdb\xe9\x0f\xdd\xb9\x79\x81\x53\xfb\x99\x3f\x96\xa9\x33\xc6\x9e\x1e\xef\x1e\xc9\xc5\xb2\x0a\x4b\x5f\x98\x68\x2e\x3f\x92\x02\x5c\xf5\x27\xfa\x2c\xb3\x40\xb4\x87\xe8\xce\x91\xac\x7d\x58\x92\x32\xf8\x12\x82\xdd\x0c\xf5\xff\x3d\x71\x5f\xfb\xa4\x2f\x06\x2f\xfa\x91\x42\xd0\x5d\xfe\x44\x34\x9f\xaa\x7f\x05\x00\x00\xff\xff\x4d\x62\xae\x61\xba\x0a\x00\x00") +var _dashboardHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x95\x51\x6f\xd3\x30\x10\xc7\xdf\x27\xed\x3b\x18\x3f\x13\xbb\x5d\x27\x6d\x8c\xb8\x02\x6d\x03\xf1\x04\x0f\x9b\x04\x8f\x8e\x7d\x49\x9c\x39\x76\x66\x5f\x52\x2a\xc4\x77\x47\xc9\x52\x48\xe9\xca\x18\x2a\x79\x49\xee\xce\xf7\xff\xf9\x7c\xf2\x25\x7d\x71\xf5\xf1\xf2\xe6\xcb\xa7\x6b\x52\x62\x6d\x97\xc7\x47\x69\xff\x26\x56\xba\x42\x50\x70\x74\x79\x7c\x44\x08\x21\x69\x09\x52\x8f\xdf\x83\x5d\x03\x4a\xa2\x4a\x19\x22\xa0\xa0\xb7\x37\xef\x92\x73\xba\x13\x2f\x11\x9b\x04\xee\x5b\xd3\x09\xfa\x39\xb9\x7d\x9b\x5c\xfa\xba\x91\x68\x32\x0b\x94\x28\xef\x10\x1c\x0a\xfa\xe1\x5a\x80\x2e\x60\x37\xdd\xc9\x1a\x04\xed\x0c\xac\x1a\x1f\x70\x92\xb1\x32\x1a\x4b\xa1\xa1\x33\x0a\x92\xc1\x78\x49\x8c\x33\x68\xa4\x4d\xa2\x92\x16\xc4\xbc\x57\x9b\xe8\xa1\x41\x0b\xcb\xf7\x9e\x5c\x63\x09\x01\xda\x9a\x5c\xc9\x58\x66\x5e\x06\x9d\xf2\x87\xe0\x64\xb5\x35\xee\x8e\x04\xb0\x82\xc6\xd2\x07\x54\x2d\x12\xa3\xbc\xa3\x04\xd7\x0d\x08\x6a\x6a\x59\x00\x37\xca\x53\x52\x06\xc8\x05\xed\xeb\x8c\x17\x9c\xc3\x28\xce\x7c\x28\x78\x2e\xbb\x3e\x89\xf5\xeb\xf8\xf6\x6e\x06\xfd\xed\x54\xa5\x5d\x15\x99\xb2\xbe\xd5\xb9\x95\x01\x98\xf2\x35\x97\x95\xfc\xca\xad\xc9\x22\xc7\x95\x41\x84\x90\x64\xde\x63\xc4\x20\x1b\xbe\x60\x0b\x76\xc6\x55\x8c\xfc\xa7\x8f\xd5\xc6\x31\x15\x23\x1d\xf7\x8e\x6b\x0b\xb1\x04\x40\x4a\xf8\x4e\x79\xcf\xc2\xe7\xde\x61\x22\x57\x10\x7d\x0d\xfc\x94\x9d\xb1\xd9\x40\x9e\xba\xff\x1f\xbc\x00\x87\x60\xc1\x5a\xc9\xe7\x6c\x31\xa2\x55\x1b\xd1\xd7\xfb\xa1\xdb\x07\x1e\x55\x30\x0d\x92\x18\xd4\x5f\x53\xab\xfb\x16\xc2\x9a\x9f\xb0\x39\x3b\x1d\x8d\x81\x56\x45\xba\x4c\xf9\x83\xe0\xf2\x10\x88\xc4\x79\xec\x39\xa7\x6c\xce\x1b\xa9\xee\x64\x01\x7a\x03\xec\x43\x6c\xe3\x3c\x34\xfe\xb2\x94\x01\x59\x15\xf9\x09\x9b\xb1\xd9\x68\x3e\xcd\x98\x78\xfa\xe7\x21\x4d\x43\x2e\x5b\x8b\x91\x15\xd6\x67\xd2\x32\x0b\x05\x38\x4d\x04\xf9\x46\xc0\xc9\xcc\x82\xbe\x20\xb9\xb4\x11\xc8\xf7\xd7\x13\xc5\xc3\x14\xb2\xef\x72\x54\xbf\xdf\x8d\x03\x1e\x5e\x6e\x3d\xf2\x19\x3b\x67\x8b\x4d\xaf\x7a\xcf\x3e\x4a\xca\x27\xf3\x33\xcd\xbc\x5e\x3f\x85\x6f\x5d\x73\x57\x0c\x4c\xe3\x72\x08\xce\xbf\x59\xb0\x57\x6c\xc6\xb5\x89\xb8\x71\x3d\xbb\xa6\x1d\xd1\x44\xf9\xba\xf1\x0e\x1c\x3e\x22\xff\x2b\xf8\x18\x68\x82\xd2\xa6\x23\x46\x0b\xaa\x37\x23\x95\x12\x65\x65\x8c\x82\x3a\xd9\x25\xb5\xee\xf3\xb4\xe9\xf6\xed\xae\x6f\x53\xeb\xb4\x85\x3f\x20\xfe\xa1\x43\x3b\x63\xa3\xda\x9a\x1a\x8f\x76\x69\xec\x4c\xca\x87\x9f\xe1\x8f\x00\x00\x00\xff\xff\x5c\x2e\xb8\x55\x1c\x07\x00\x00") func dashboardHtmlBytes() ([]byte, error) { return bindataRead( @@ -109,6 +89,26 @@ func dashboardHtml() (*asset, error) { return a, nil } +var _jsBundleJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x3d\x6b\x73\xdb\x38\x92\x9f\xe3\x5f\xd1\xe1\x56\x8d\xa5\x0c\x25\x59\x72\x5e\x25\xaf\x77\x2a\x71\x92\xdb\xdc\x4d\x1e\x95\x78\x6f\xea\xca\xe5\x62\x41\x24\x24\xe1\x4c\x02\x3c\x02\xf4\xe3\xb2\xfe\xef\x57\x78\x91\xa0\x48\xca\xb4\x4d\x67\x66\x6e\xa3\xaa\x19\x4b\x04\xd0\xdd\x68\x74\x37\xba\x1b\x0d\x66\xf2\x44\x7d\x26\x30\x58\xe6\x34\x14\x84\xd1\x41\xc2\xa2\x3c\xc6\x7c\x08\xdf\x60\x32\x81\x0b\xbc\x48\x51\x78\xf6\x9a\x31\xc1\x45\x86\xd2\x9d\x62\xc4\xa3\xc9\x04\x8e\xd7\x18\x74\x7f\x08\x51\xb8\xc6\x4e\xeb\x39\xca\x80\x50\x2e\x50\x1c\xe3\xe8\x83\x86\x09\x87\xf0\xed\xfa\xa0\xe8\x54\x87\x95\xe1\xff\xc9\x49\x86\xc1\x12\xe3\xf4\xb0\x8f\x20\x08\x0c\x4d\x81\xe9\x1d\x04\x86\xe6\xf7\xd1\x10\xbe\x35\x41\x97\xe0\x8f\xd6\x38\x3c\x03\xb2\xb4\xf4\x12\x0e\x84\xd6\xa8\x7e\x44\x96\x83\x4d\xaa\x4f\x2c\xf4\x53\x17\x3c\x3c\x7a\xf4\x28\xc3\x22\xcf\x68\x6d\x9a\xe5\x80\x31\xbe\x4c\x59\x26\xf8\x81\x3b\xec\x7a\x93\xb2\x0c\x23\x81\x01\x01\xc5\x17\x96\xba\x01\xa2\x11\xa4\xb9\x00\x22\x80\x50\xc1\x40\xac\x0d\x8b\x87\xee\x68\xc9\x64\x33\xe2\x70\x0b\x19\x92\xef\x15\xc2\xc9\x1c\x6c\xa3\x5f\x69\x88\xe7\xb0\x44\x31\xc7\xd5\xa7\x66\x16\x73\xf8\x56\xa1\xbd\x79\x29\xe5\x94\xde\x5e\xe2\x30\x17\x58\x51\x6d\xe8\x6b\x58\xd2\x47\x49\x8d\x5f\x21\x8a\x63\xb3\x9a\x96\x77\xbe\x81\x60\xff\x96\xcf\x1b\x24\x61\xd8\x4a\xd2\xbb\x18\xad\x5c\x7a\x10\x87\x98\xa1\x08\x47\x75\x82\xc6\x31\x1c\x82\xc8\x72\xdc\x0a\xec\x8b\x5e\x78\x09\xce\x50\x03\x6c\xe9\x40\x77\xbb\x1b\x21\xa9\x12\xef\x0a\xc4\x75\x1d\x4b\x55\x33\xe4\x18\xee\x32\x93\x03\x5b\xfc\x37\x0e\x05\x0c\x4a\x16\x98\x96\x20\x70\x05\xa4\x81\x43\xe3\x04\x0e\x2d\x98\x36\x55\xac\x21\xac\xe9\x49\x13\xe0\xb0\x41\x06\xdb\x30\x44\x78\x49\x28\x86\x15\x16\x02\x67\x85\x6c\xc0\x92\x65\xb0\x46\x59\xc2\xe8\x95\x65\xec\x0d\x48\x23\x38\x2c\x86\x0f\x0a\xc9\xa0\x28\xc1\xbe\x81\xbe\xa1\xb4\x64\x39\x78\xdc\x04\x88\x55\x47\x0f\x37\x75\xfd\x93\xe2\xf8\x58\x13\xfe\x39\x63\x29\xce\xc4\xd5\x26\xc6\xea\x90\x47\x21\xa3\x4b\xb2\xca\x33\xb4\x88\x71\xa3\x62\x3d\xc2\x34\x4f\xb0\x69\x97\x12\xb7\xd1\xbc\xc2\x62\x6e\xa6\x51\x69\xb8\x1e\xb6\x5a\x94\x56\xfb\xba\xc2\xe2\x0d\x5e\xa2\x3c\x16\x6f\x15\xd1\x55\xae\x87\x2c\x49\x91\x20\x0b\x12\x13\x71\x05\x17\x44\xac\x81\x32\x3a\xb2\x8b\x61\x04\xe6\x86\xc5\xa0\xee\x62\xe8\x21\x1b\x6c\x94\x06\xcb\x2c\xba\x95\x42\xf8\xe9\x27\xab\x1c\x41\x80\xb9\x16\x1c\xf8\xa5\x32\xdf\x82\xd4\x72\x12\x03\xb9\x41\x55\x74\xeb\x64\x37\xd2\x4d\xbb\xa7\x07\x70\x0d\xf3\x56\x08\x1a\x85\xe6\x02\xaf\xc3\x39\x80\xeb\x0a\x77\x1b\xc5\x6e\xa0\x67\xe1\xc3\x2e\xda\x2d\x24\xed\xa0\x41\xef\x75\xd3\x41\x97\x15\x32\x22\x96\x66\x4c\x30\x71\x95\xe2\xf1\x1a\xf1\x4f\x17\xd4\x0a\x9b\x32\x8e\x37\xac\x00\x73\x57\x40\x1b\x09\x1f\x52\x03\xc0\x99\x69\x17\x54\xf5\xf1\x15\xce\x54\x69\x2f\x89\x49\xf3\x45\x4c\xc2\x20\x45\x62\x1d\x04\x37\x90\x9b\xc2\x21\x78\x5e\x1b\xcc\x5f\x19\x8a\x00\x53\x91\x5d\x15\x46\x9b\x46\x76\x06\x75\xf3\x60\x1a\x9a\xfc\x83\x26\xdc\xd2\x1b\xd9\x77\x97\xec\xba\xb0\x9c\xf7\xff\x38\x33\x19\x9c\xec\x4c\x9e\xc0\x1e\x98\x67\x75\x57\xcb\xb7\x73\x91\xba\xb2\x53\xdd\x27\xe0\x10\xde\xd3\x25\xce\x28\x3b\xd8\x31\xc3\xaf\x87\xd2\x48\xc0\x74\x1b\xc0\x72\xbe\x06\x4e\x10\x34\x6f\x97\x12\xa3\x97\x73\x0c\x5c\x64\x24\x14\x6a\x29\x36\x2c\x30\x0c\x16\x84\x46\x84\xae\x86\xf0\x64\xd2\x04\x64\x1c\x0d\x1a\xf1\x79\xa1\xe7\x97\xc2\xe8\x08\x1f\xe1\x1f\xf3\x38\xfe\x94\xfd\x83\x6a\x6b\x1a\x1d\x80\x36\x68\xbd\x61\x8e\x5a\x30\x27\x28\x3d\x5a\x93\x38\xca\x30\xed\x1d\x27\x6a\xc1\x79\x14\x63\x94\x2d\xc9\x65\xef\x08\x17\x2d\x08\x3f\xbc\xfd\xf0\xe9\xcb\x7f\x05\x5f\x5f\x7d\xf8\xfc\xeb\xdb\xe0\xd7\xf7\x1f\xde\x1f\x17\xb8\x73\x9a\x73\x1c\x6d\x92\x70\xfc\xe5\xd5\xbb\x77\xef\x8f\x2a\x43\xb4\x78\x15\x3d\x49\xa2\x7a\x3e\x99\x80\x34\xe1\x41\xf0\xdb\xdb\xd7\x9f\x5f\x1d\xfd\x47\xf0\xfe\xc3\xe7\x4f\x5f\x8e\xdf\xbe\x09\x3e\x7c\x7a\xf3\x8f\x5f\xdf\x06\x7b\x01\xd1\xf2\x1a\x04\x70\xd8\xa8\x8b\x7b\x55\x2e\xdc\x01\x70\x60\xec\x7c\x33\x82\x31\x1d\x74\x82\x22\xc9\x98\xd4\x65\xd1\x70\x91\xab\xed\x58\xc6\x0b\x48\x12\x46\xe4\x16\x2d\x63\x06\x9a\xc7\x31\xb0\x0c\x72\xdb\x7f\xbc\xa3\x22\x9d\x1a\x98\xd2\x0e\xd7\x1b\x07\x16\xa2\xd4\x3f\xb0\xeb\x56\xa0\x39\x3c\x3c\xd4\x78\xfe\xf9\x4f\x90\xc6\x99\x2d\xab\x6d\xbb\x05\xf2\xdd\x83\x9d\xeb\x83\x1d\x45\x81\x23\xda\x2e\x6e\xe7\xf1\x20\x34\x5f\x7c\xf9\xf4\x5d\x4e\xc3\x0a\xfa\xc7\xaf\xb2\x0c\x5d\x8d\x09\x57\x7f\x8b\xce\x43\x49\x85\xfd\x31\x8e\x31\x5d\x89\x35\xfc\x15\xa6\x4d\x8f\x25\x71\x53\xf8\xc5\x82\x77\x60\xcc\xcb\xbe\x09\x4a\x07\x16\xbf\x26\x5f\xd1\x6f\xd5\xc4\x25\xde\x3e\x1b\x54\x08\xd5\x7b\x57\xb7\x45\x3e\xf1\x42\x15\x5d\xfd\xe7\x47\x16\x61\xef\x74\x38\x98\x49\xeb\x40\xce\x3d\x69\x9e\x0c\x74\x6f\x58\x72\xb1\x41\x77\xe0\x10\x66\x7b\x7b\x07\x32\x18\xfe\x80\x2e\x49\x92\x27\x40\xf3\x64\x81\x33\xe9\xf2\x27\x38\x61\xd9\x15\x44\x48\x20\xe0\x28\x49\x63\xcc\xb5\x40\x34\xaa\xd4\x36\x48\x22\x43\xcb\x25\x09\x37\x40\x55\x6d\xfe\xec\xbe\x9b\xc8\xf8\x88\x25\x29\xa3\x98\x8a\xcd\xed\x64\xff\x81\xb6\x93\x66\xb7\xb9\xd9\xa0\x95\xee\x9f\xe7\xc3\x37\x38\x47\x71\x6e\xdc\xe2\x4d\xd3\xf9\xff\xd3\x1a\xdd\x9e\x8c\x69\x10\x84\x76\x45\x79\xf0\x06\xf1\xf5\x82\xa1\x2c\x6a\x9d\xed\xd3\xa1\x54\xb7\x1d\x15\xc1\xd2\x08\x67\x5c\xc5\x78\x17\x6b\x16\x63\x88\xec\xe8\xf1\xce\xce\xad\x26\x3e\x46\xe3\x4c\x41\x1b\xdc\x4b\x33\xa7\xcf\xfd\xbb\xcc\xf4\xc4\x43\x1e\x4c\x9e\x80\x5d\x85\x27\x93\xd3\xa1\x0f\x11\x0b\xf3\x04\x53\x31\x5e\x61\xf1\x36\xc6\xf2\xeb\xeb\xab\xf7\xd1\x60\xb7\x98\xe5\xee\x70\xb8\xa9\x03\x4f\x1f\xde\xa5\xea\x2e\x5f\xc5\x6c\x5b\x17\x73\x76\x57\xd1\x75\x40\xdf\x4b\x78\x1d\x38\x77\x16\xdf\x23\x96\x24\x8c\xb6\xce\x71\x7a\x37\xc0\xb3\x20\xf8\x4a\x22\xfc\x1a\x65\xad\x90\x9f\xdd\x0d\xf2\x7e\x10\x1c\xb3\xf4\x23\x3a\x27\x2b\x24\xe5\xa3\x15\xfe\xf3\xbb\xc1\x7f\x1a\x04\x9f\xd1\x0a\x1f\x31\x2a\xb6\xad\xfd\x8b\xbb\x41\x7f\x16\x04\xef\x18\x13\xb8\x9d\x2d\x2f\xef\x06\xf8\xf9\x43\x19\xda\xe7\xbd\x18\xda\xe7\x15\x43\x5b\xa6\x92\xc3\x18\x71\x7e\x84\xe2\x58\x65\x87\x75\xe6\x97\x86\xd8\x87\x23\x46\xb9\xc8\xf2\x50\xb0\x4c\x7a\xd6\x64\x09\x83\xc7\x45\x33\xd8\x2f\x6c\x59\xe9\x28\x7b\x8a\x75\xc6\x2e\x54\x26\xf7\xf8\x2a\xc5\x6f\xb3\x8c\x65\x03\xef\x08\x51\xca\x04\xc8\xa0\x1a\x10\x28\xa4\x80\x38\xa0\xc2\xd3\xf1\x64\x68\x0d\xd7\x3b\x0e\x69\x29\xe3\x9c\x2c\x62\xec\x20\xd0\x59\xc7\x01\xc7\xf1\xd2\x57\xc0\x0a\xd2\xe4\xa3\x2a\xf6\x2f\x78\x89\x33\x4c\x43\x4b\x82\x58\x13\x0e\x6b\xc4\xe9\xae\x80\x05\xc6\x14\x08\x25\x82\xa0\x98\xc8\x70\x60\x04\x3c\x4f\x71\x36\x18\x56\x7a\x84\x2a\x91\xa7\x49\x33\xae\x97\x9a\xc1\x4f\x3f\xc1\xc0\xb8\xa5\xea\xb7\xf4\xfa\x3c\x9d\x2b\xf0\x1c\x97\xb5\x6c\x2b\x67\x09\xbf\xe8\xc7\x73\x90\x14\x1f\x54\x67\x4c\xe8\x1a\x67\x44\xf0\x01\xcf\x17\x47\x92\x47\xbe\x26\x4b\x7d\xb7\x53\x35\xc0\xcb\x06\x78\x5c\x41\x21\xa9\xdb\x68\x94\xae\x74\xeb\xd2\x7c\x95\x7d\xa5\xef\x94\x61\xce\x95\xc3\x9c\x73\x01\x98\x88\x35\xce\x60\x81\x0b\x7f\xbf\x5c\x2b\x1f\xe4\x5a\x7a\xf0\x33\xd4\x68\x51\xac\xb2\xd4\x97\xc9\x15\x38\xb4\xf9\x16\xbd\xe5\x0d\x1c\x02\x2b\xe4\x96\x43\xa4\xff\x13\x96\x2b\x3f\x2f\xdd\xa1\x92\x39\x6e\xee\x50\xe7\x16\xe1\x22\x23\xc2\x49\x26\x42\x35\x01\xa9\x3d\x29\xe9\x4b\x29\x56\xba\xcc\x35\xf4\x71\x2c\x3e\x5b\x12\x3e\x2d\xe1\x97\xe6\xe7\x2d\x0b\x54\xd2\x36\x0e\x02\x35\x13\x65\x0c\xca\x2e\x6a\xbd\xcd\x67\x32\x81\x62\x13\x97\x71\x95\xca\x35\x23\x42\xa1\xd8\x54\x7c\xb8\x58\x93\x70\x0d\x59\xcd\x53\x49\xd1\x0a\xfb\x12\x42\x82\xce\x30\x97\x93\xa4\x58\xcb\x90\xca\x5b\xca\x8e\x1c\x67\xe7\x38\x53\xf9\xa2\x98\x70\x81\x29\x57\x59\xce\x04\x73\x8e\x56\xd2\xb1\x9e\x4c\xe0\xb7\x35\x56\x29\xfc\x4c\x05\x76\x48\xaa\x44\xc8\x12\x42\x57\xb6\x9b\x0f\x79\x1a\x21\x81\x35\x6e\x89\x75\x57\x61\x93\x96\x19\x42\x96\x65\x98\xa7\x4c\xc5\xee\xf1\xd5\xd8\x84\x31\xe5\xa4\x9c\x38\x66\x10\x14\x7e\xb7\x8e\x64\xc0\x91\xf6\x62\x84\x0f\x4e\xb7\x83\x1d\xd5\xad\x00\x51\xf4\x1a\xa4\x19\x4b\xb9\x05\xa3\x40\x6d\x58\x31\xa9\xea\x7e\x39\xc0\x82\x92\x1f\x65\x6b\x95\x29\x38\xdc\x66\x61\x34\x84\x92\x1a\x9d\x0e\xd4\x4f\x35\x7a\x17\xa8\x02\x38\xd6\xac\x3a\x5a\x23\x1d\x70\x94\x73\x4f\xf8\xca\xa5\xd6\x92\x61\x22\xa7\x43\x33\x9c\x0b\x24\xf0\x38\x54\xc3\xc7\xba\xed\xa0\x36\xc6\xc6\x48\x8d\x83\x4c\xa3\x43\x98\xfc\x4c\x26\xf0\x8e\xc4\xb1\x5a\xc1\xc2\xf7\x2b\xe5\x24\x45\x5c\xa8\x90\x6b\x0c\x09\x96\xfe\x1a\x97\xb2\xc0\xb1\x00\x46\xe3\x2b\x20\xfa\x8c\x67\x49\x32\x2e\x20\xe1\x2b\x7f\x13\x36\x5a\x0a\x9c\xe9\xc3\xb9\x52\x0a\x09\x07\xcc\xa5\x1e\x12\xbe\xc6\xd1\xb8\x32\x46\x2a\x5e\xc2\x57\x63\x8b\x4d\x1a\xa8\x22\x9c\xdf\xe4\x93\x41\xa2\xc2\x60\x85\x04\xc9\xd0\x9c\x83\x60\x90\x66\xf8\x5c\x8a\xa1\x0a\x17\xa5\x9a\xe7\xbc\xa2\x00\xb2\x9d\xb0\xdc\xd5\x8e\x71\x0d\xb6\xe6\xf3\x38\x46\x0b\x1c\xcb\x45\x3b\x39\x3d\xa8\xf5\x31\x6c\xdd\xde\xc9\x00\x92\xc4\x70\x2c\xf8\xc9\xde\xa9\xfa\x7e\x03\xc8\x96\xee\xb5\xfe\x46\x5c\xe0\x10\x1c\xce\x35\x0a\x49\x45\x50\x36\xfa\xa7\x19\x0b\x31\xe7\x2c\x53\x01\xf8\xf1\xa7\x37\x9f\x60\x70\x96\x67\x67\x2c\x21\x9c\x0c\xe7\xf0\xf8\xf1\xe3\x3a\xea\xc9\x04\x3e\xe7\xa2\x2a\x2a\x60\x8e\x63\x17\x78\x45\x28\x95\xf6\xc2\x9c\xfd\xe9\xd5\x19\xc3\xb1\x54\x30\xb3\x40\xbc\x69\x71\x28\xbe\x54\xe2\xa4\xc0\xf9\x4d\x48\xb5\xe9\x5b\x31\xcc\x2d\x36\x4c\xa3\xfa\x02\x4a\x9b\x36\x50\xf9\x27\x39\x59\x9c\xd8\x44\xcc\x08\xa6\x07\x40\xe0\x6f\x87\xb0\x27\x77\x98\x4e\x1e\xf8\x89\xb7\x50\x81\x54\x53\x0a\xe4\xc9\xe4\x14\xfe\x56\x15\x17\x83\xea\x00\x46\x23\xd2\x24\xb7\x35\xf9\x1a\xe7\x94\xaf\xc9\x52\x0c\x12\x9c\x9c\x90\xd3\xb1\x20\x09\x1e\xf3\x7c\x21\xc3\x24\xba\xaa\x3c\x2d\x66\xf1\x4c\x1a\x9a\x26\xc8\x55\xa9\xec\x15\x74\x8b\x2c\x6f\xe2\x50\xdb\xf1\x0d\xd4\xb5\xc2\x50\x1d\xb6\x41\xb9\xae\x8b\xa2\x31\x79\x58\x7c\x95\x56\x6f\xf0\x0d\xb4\xdd\x93\xae\x81\x26\x79\x6e\xfe\xfa\x16\xff\xbc\x30\x98\x7a\xd3\x9f\x4c\xe0\x1f\xca\x4a\x1b\x83\x65\xc3\xe9\xba\x5c\x69\x77\xaf\x4a\xd6\x75\xcd\xb0\x5a\xcd\x90\x2e\x95\xab\x18\x98\x46\x1b\x2a\xd1\x62\x02\xd5\x0e\xd0\xc5\x02\x7e\xc1\x09\x3b\xc7\x8e\x2d\xc6\x3a\xa6\x37\x35\x1c\xe6\xac\xda\x64\xcd\x76\x01\x25\x2c\xa7\x02\xf0\x65\x88\x71\xa4\x77\xef\x98\x24\x44\xd4\x27\xaa\x68\x69\x10\x6b\xe5\xb8\xf6\xa1\x36\xdd\x54\x43\x4b\x45\x37\x59\xdf\xda\xb7\x4d\x78\x3b\x21\xe8\x3e\xea\x7a\xfb\x56\x32\x4e\x73\xbe\x76\xd6\xb8\xa6\x90\x1b\x2d\xdb\x95\x72\x63\xfe\xbd\xc2\x6e\xe3\xd7\x26\x92\x36\x3d\x6d\x65\x5d\x01\xa0\xd8\x76\x0a\x18\xbf\x9b\x66\x97\xab\x76\x5d\x73\xdf\x32\x6c\x1c\x85\x8a\xef\xd6\xe4\xb8\x19\xd7\xfa\x50\xa9\xfd\x6f\x78\xf1\x95\x85\x67\x58\x0c\xbc\x0b\x3e\x9f\x4c\x64\x58\x14\xb3\x50\xa5\x46\xc6\x6b\xc6\x05\xfc\x0c\xde\x04\xa5\xc4\xdb\x9c\xb8\x06\x33\x66\xd4\x38\xdb\x15\xbc\x6a\xeb\x6c\x52\x1d\xe5\x0a\xf0\x15\x1c\xc2\xbf\x7f\xfd\xf4\x71\x9c\xa2\x8c\x63\xdd\x5b\xb1\xbd\x61\x85\xa4\x86\xdf\x98\x97\xac\xe8\x73\xa8\xf4\xb9\x7e\xa2\x23\xb5\x59\x79\xb3\x6d\x3a\xdd\x64\x35\xa1\x51\x5b\xea\x2e\xb3\x02\xbc\x61\x70\xdb\x38\x16\xc6\x8c\xe3\xad\xeb\x54\x52\x23\x7d\xd9\x63\x92\x60\x96\x8b\xc1\xc6\x4a\xfb\xb0\xbf\xb7\xb7\x57\x47\xda\x84\x5f\x0f\x2d\x04\xeb\x0d\x89\x3e\x28\x13\xbb\x8d\x08\x7b\x1e\x5f\xc5\xea\x9a\x93\x26\x31\x94\xc1\x5e\x17\xb0\x37\xae\xe9\xf3\x8e\xa7\x40\x8c\x0a\x44\xa8\x8c\xf6\x59\x74\xe5\xf9\xbd\x01\x96\xd1\x6c\x50\x40\xf7\x7c\x38\xb9\x17\xe4\xed\xe9\x71\x37\xe1\x69\x33\xe2\xe6\x81\xc9\x88\x3f\x20\xf2\x7a\x4e\xd4\x92\x50\x79\xfc\xf0\x84\x6c\x26\x4f\x1b\xce\x06\x7c\x95\xd1\xb1\xff\xaf\x6b\x8d\xa7\x6d\xaf\x37\x6f\x08\x31\xab\x8a\xf2\xb0\x53\x71\x32\xb5\x76\x16\xfa\xb7\xe2\xe2\xe9\x70\xab\x16\x29\xa2\x55\xd1\xa7\x4b\x71\xb9\xab\x4c\x26\xf0\x55\xb0\xcc\x24\x36\x74\x6f\xe3\xb0\x99\x60\xba\x65\xa3\x9c\xb7\x58\x3e\xbd\x35\xcf\xe1\xe4\xb4\x1e\xcd\xc8\x8f\xdd\x1e\xe7\x70\xd2\x0c\xa0\x00\x32\x07\x8f\x5f\x71\x81\x93\x89\xc6\x38\x21\x34\xe7\xd8\x6b\x06\x2b\x3f\x0b\x14\x9e\xad\x32\x96\xd3\xe8\x88\xc5\x2c\x9b\x83\x97\xad\x16\x68\xb0\xff\xd2\x87\xe9\xcb\x67\x3e\x4c\x9f\x3d\xf5\x61\x6f\xbc\x3f\x1d\x6e\x03\xc2\xb2\x08\x67\x5b\x01\xbc\xd8\x36\x3e\x65\x84\x8a\xd7\xbd\x00\xe9\x36\x9d\x9b\x01\xfd\x9d\x9d\xe3\xac\x0e\xed\x2f\xcb\xe5\xb2\xdb\xd0\xfa\x6c\x66\xb3\x3d\xdf\xfe\xb7\x95\x9d\x0e\x3b\x7e\x23\x91\x58\xcf\x61\xda\xde\x59\xca\x86\x94\x9c\xc6\x0e\xd7\xf5\xc7\xd7\x75\x50\x85\x57\xf4\x1d\xa4\x33\x9d\xa5\x93\xf7\x74\x21\x79\x7a\xac\xd1\xde\x41\x38\x7d\x78\x29\x17\x74\xef\xb9\x12\xcd\xdb\x4a\x66\x65\xf4\x8b\xbd\x3b\x09\xe6\xad\x61\x74\x99\x49\x07\x38\x0f\x20\x96\xd3\x67\x53\x7f\xfa\xf2\x85\x3f\xdb\x7b\xf6\x7b\x8a\x65\xbb\xab\x6d\xbf\xba\x0e\x91\x7e\x6a\x62\xea\xc9\x04\x2a\xe9\xd3\x57\x14\xc5\x57\xff\x6b\xcc\x73\x3d\x2b\x8d\x68\x54\xc9\x4c\x6b\x9b\xbd\xbb\x2d\x35\x6d\xb0\x94\x5e\x7e\x99\xa0\xe4\x80\xe0\x02\x2f\xb8\x72\xe3\xb7\x24\xd3\xfd\x4a\x26\x7d\x93\x2a\x6e\x51\x48\xe2\x44\x46\x74\xe2\xaa\xc4\xc7\xa8\x0b\x3a\x66\x9c\x3b\x64\xd5\xfd\x4a\x7d\x3a\x65\x27\x58\x10\x2b\x63\x7e\xbb\x53\xe9\x44\x40\x23\xe5\x6e\x56\xd6\x40\x06\xc2\xcd\x49\x02\x8e\x2a\x0c\x51\xfe\xa6\x7b\xc6\x50\x46\x4f\x16\x93\x5b\x19\xb1\xe3\x2c\x64\x91\x62\x3f\xd8\xb9\xee\x7a\x50\x5e\x16\x4d\xe8\xf2\x83\xe2\x40\xd4\xfa\x28\xa6\x24\xaf\x52\x0b\x58\x54\x1c\x48\x57\xe0\x14\x0e\x61\x50\xc9\xee\x57\x8a\x18\x9e\xfd\xf1\xeb\x42\xdb\x2a\x25\x8d\xc3\x5a\x2b\x94\xfc\x51\x34\xf1\x9d\x8a\x26\x1e\xe8\x0c\x7f\xd6\xcb\x19\xfe\xec\xc7\x19\xfe\x8f\x33\xfc\x1f\x67\xf8\x7f\x90\x33\xfc\xc9\x04\x3e\x60\x9a\xbf\x17\x38\x29\x76\x4f\x44\x81\xc8\xdf\x4b\xc5\x6b\xd9\xec\xec\xbf\xca\x2f\x50\xa7\x66\x31\xa3\x2b\xe9\x39\xf0\x7c\x91\xc8\x3e\x72\x0c\xf7\xe5\x74\x9c\xc3\xf8\xe2\x40\xbd\xc0\xd2\xf5\x3c\xdd\x0e\xd8\x7a\x9c\x6e\x3b\x55\xb2\x4b\xfa\x74\x1c\x27\xa9\xaf\x7d\x34\x1f\x82\x0c\x0b\x37\xba\x6e\x3c\x69\xb7\xb0\xdc\x0c\x67\x71\x30\x17\xc4\xaa\x1e\x1b\x65\x2b\x55\x8f\x68\xcf\x17\x7c\xf9\x84\xc3\x21\xe8\x62\x6b\xd9\x6b\xe8\x43\x70\x86\xaf\xe0\x10\xf6\x0e\xf4\xb7\xbf\xaa\xd1\xfa\xc7\xcf\x3f\x6f\xe6\xc1\x24\x80\x13\xd9\x74\xea\xc2\xd7\x4f\x9c\xe4\xc0\x4e\xcd\xfd\xcc\xb0\x34\xbf\x03\x35\x53\xfd\xe5\x6e\x05\x01\x63\x94\xa6\xf1\x95\xb3\x16\x3e\x9c\xc8\x8e\xa7\xe3\x90\xd1\x10\x89\x81\xa4\x70\x38\x1c\x1a\x6e\xda\xbf\x7d\xe6\xf8\x66\x37\xa4\xe2\x62\xe2\xd9\x54\xcf\xcd\xd9\xb7\x9b\x80\xa1\x1e\x61\x49\xba\xbc\x25\x52\x26\x48\x73\x45\xd5\x55\x8c\x95\x80\x7d\x44\x09\x2e\xb8\xa5\x9f\x0b\x7c\x29\x3a\xe4\x9b\x6e\x42\x6b\x52\x93\x4b\x04\x4b\x34\x0a\xd7\xf8\x3c\x63\x74\x14\xb1\x0b\xea\x0d\x4f\x87\xb5\xe2\x06\x5d\x23\x0c\x51\xc6\x52\xd9\x07\x94\xb6\xea\xba\x08\xab\xab\x28\xc3\x65\x65\x7f\x65\x7c\x8f\x39\xf7\x0a\x83\xdc\x6b\x09\xf7\xe6\x47\x1e\x4b\x76\x50\x74\xae\x27\x11\xc8\x19\x76\x49\x01\x57\x68\x8f\x14\xed\xee\xfd\x8b\x56\xaa\x7d\x47\xe6\xd5\xb3\x2d\x89\xfb\x3e\x85\x5f\xe3\xda\xc8\xf4\x0f\x87\xa7\x6e\x16\xd1\xd7\xb6\x4f\x0a\x5e\xbb\x21\x70\xec\xe2\xb0\x12\xc2\xda\xbb\x47\xc6\x16\xde\x3d\x22\xd2\xfb\x4a\xb9\xa7\x68\xa9\x2b\xfa\xbb\xdb\x82\x6b\x42\xe4\xef\xc2\x8c\xf4\xc5\xc1\x52\x3c\x38\x89\xf0\xc8\x48\xc7\xfd\xb4\x7f\xfa\xbc\xdc\x31\xda\x92\xd1\x5e\x61\x06\xbc\xb9\xd4\xd6\xd1\x9a\x25\x6e\x0e\xd4\x93\xe6\x40\x36\xfd\xbd\xfa\xdc\x8a\xd9\xbc\x57\x6b\xe7\x15\xc1\xe6\xd4\xdb\x4c\x99\x7b\xeb\x0c\x2f\x25\x25\x45\x8c\x3c\x1d\xaf\x45\x12\x7b\xa5\x5c\x75\xc9\x93\xdf\x85\x98\x59\x07\x62\x66\x35\x62\x74\xe2\xe6\xde\x44\xdd\x69\x15\x71\x44\x44\xd3\x2a\x7e\xc4\xe2\x82\x65\x67\x84\xae\x1a\xd7\xb2\x4f\xee\xb9\xa8\xda\xb8\x47\x8b\x3e\x35\xee\xfd\x8e\xcc\x8b\x30\x3f\x13\x2c\x6d\xe2\xdf\xf1\x65\xca\x58\xfc\xe0\xbc\xb3\x68\xda\xf8\x26\x54\xfb\x1f\x89\x67\x2a\x8e\x68\xe2\xd8\xaf\x6c\xc5\x1f\x9c\x5f\x1a\x49\x1b\xb7\x62\xb6\xe2\x7f\x24\x5e\x85\x31\xa3\x8d\xbc\x7a\x1d\xb3\xf0\x2c\x5c\x23\x42\x1f\xde\xd2\x96\xb8\xb6\x98\xda\x45\xd9\xe9\x81\x6d\x6d\x49\xce\x16\x63\x5b\x92\x53\xb7\xb6\x0f\x45\xce\x7e\x17\x72\xf6\xbf\x1b\x39\x4f\xbb\x90\xf3\xf4\xbb\x91\xf3\xac\x0b\x39\xcf\xfe\x50\x5b\xe3\x02\x65\x23\x75\x8c\x31\x62\x4d\x3a\xf8\x55\x1d\x09\x7b\x86\xc0\x53\x73\x61\x77\x32\x29\x6a\x1c\x4a\x77\x51\x7a\x6a\x0b\x94\xb9\x1e\xa3\xf4\x17\x6d\x47\xc7\x65\x34\x8f\x7a\xf7\x1a\x8b\xc2\x92\x78\x94\x44\xa3\x7d\x88\xf1\x52\x04\xa1\xda\x39\xfa\x82\x6d\x41\x02\x0f\x33\x16\xc7\xa3\x73\x82\x2f\xee\xed\x99\x3a\xf0\x29\x3a\x97\x3c\xa4\xe8\x3c\x10\x44\xa8\xfb\xb8\xbd\x88\xaa\xc7\x89\xc0\x05\xc8\xfe\xc2\xe8\x25\x1a\xa5\xe8\xc2\xeb\x43\xa3\x78\x8a\x68\xa9\x54\xff\xc6\xe0\xad\x8a\x74\xf3\xa4\x3c\xee\xf1\x86\xa7\x15\x61\xae\xb9\x9d\x8e\x6a\x49\x75\x72\xba\x72\x71\x15\x4b\x99\xff\x66\x0e\x76\xe7\xb0\x67\xce\x0a\x7b\xd1\xbb\x8e\x11\xab\xae\x27\x29\x6e\xdc\x77\xac\xcb\xe9\x28\x3b\xaa\xee\x49\x86\x4a\x81\x54\x45\x58\x93\x28\xc2\x74\x94\x66\x84\x0a\x28\xda\x7a\xd4\x05\x8d\x4a\x9f\xfc\xdd\x17\xac\xb5\x5d\xc3\xea\xaa\x91\x48\x2e\xaf\xb1\x2c\x3a\x0c\x2c\x4c\x91\xb5\x45\xee\x19\xdc\xf3\x3f\xef\x19\x5c\xa5\x62\xeb\x1e\x27\x71\x7f\xa2\x7b\xf7\x72\x1f\xa9\x16\xaa\x95\xbb\x89\x60\xa9\xb4\x82\xb6\x61\x63\x53\xa9\x8e\x72\xb6\x96\x4a\xc3\xad\x37\x98\x8e\xef\xaf\x10\x2c\x0d\x28\x3a\xef\x22\xf3\x1d\x21\x4a\x7b\xdf\x55\x3b\x6f\x02\xa9\x29\xf3\xfa\xa5\x0e\x04\x5b\xad\xba\x6d\x47\x37\x01\x75\xb2\xb9\x1e\xf4\x01\xd0\xd9\x8b\x16\x28\xe3\x9b\x9b\x84\xb6\x21\x81\xfe\x28\x9b\x65\xe6\x62\x2c\x49\xd5\xe2\x64\x4c\x6d\x13\x5e\x29\x7b\x45\xbf\x26\x83\xf3\xe2\xc7\x9b\x0b\x7e\x1c\xc2\xff\x38\x84\xff\x71\x08\xff\xe3\x10\xfe\x5f\xec\x10\x5e\x55\x0c\xaa\xb7\x8f\xaa\x73\xf3\x38\xc3\x28\x52\xf7\x96\x73\x8e\xc7\x45\x87\xe2\x88\xd6\x71\x6d\x54\xb8\xbd\x71\x40\x6f\xab\x0a\x89\xba\x7d\xaf\xaf\xc7\x43\x86\x63\x24\x70\x64\xae\x1a\xaf\x11\x55\x17\xe8\xcd\x5b\xbb\xaa\xd0\xbb\x9e\xd1\x57\x87\x6d\x3d\xa9\xaf\x76\xed\x7a\xfb\xbd\x3a\xea\x3b\x5d\x81\xbf\xc7\xf5\x18\xe7\x9a\x81\xb9\x59\xa5\x66\x60\x0e\x11\xd5\x35\xe1\x86\x93\x42\x29\x45\xf3\xea\x39\xb1\x14\xf9\x5a\x3f\x5d\xcb\xea\xf6\x93\x4f\x36\x4e\x03\xef\x79\x2d\x47\x99\xcf\xde\x0f\x7d\x1d\xbe\x34\x5e\xbb\xaa\xf5\x32\x97\xaa\x36\x2f\x2d\x6e\x5c\x5e\xed\x37\xd5\xd3\x78\x80\xdf\x5f\x80\x7b\x19\xa4\x88\xe2\xb8\xcf\xfc\xce\x65\x8f\x39\x98\xf5\xac\xf0\xa4\x37\x2b\x16\xfa\xc8\x2a\x94\x67\xb0\x3a\x29\x35\xca\xc8\x6a\x2d\x40\xb1\x24\x10\x8c\xc5\x0b\x76\xd9\xcb\x3c\x9c\xe3\xf2\x7e\x72\x5d\x21\x8b\x63\x94\x72\x3c\x8a\x09\x3d\xeb\x23\xe1\x41\xea\xb5\x1b\x79\xea\x0d\x87\xff\x8a\x95\x1b\x6a\xb5\x3c\x3b\xc1\x5e\x04\x00\xb9\x10\x47\xdd\x23\xcd\x5b\x2c\xdb\x85\xf4\x24\xd7\xde\xb0\xf1\xe6\x99\x4d\x23\xfe\xa5\xe1\x0a\x83\x27\x0d\xb6\xa5\x69\xee\x4e\xbc\xde\xd5\xc6\x8e\x8b\x5c\x08\xd6\xd8\x03\x65\x04\x8d\xf0\x65\x8a\x68\x84\x23\x9d\x81\x8f\x39\xf6\x36\x36\x84\xde\x94\xb7\xe0\xe9\xbf\x56\x79\x4d\xe3\x22\xdb\xc5\x29\x93\x88\xe5\x80\x5e\xb2\xb0\xfd\x9b\xb1\x98\x3d\x90\x0d\x93\x80\xa5\xf9\xba\xf7\xbc\xef\x97\xfa\xee\x33\xf9\x7d\x19\x98\x4b\x38\x7d\xf0\x2a\x44\xf4\x1c\x71\xaf\x7a\x88\xe6\xfe\xbf\x94\x76\x75\xf9\xbd\xd5\x3f\x32\x2f\xd7\x91\x7f\x1a\x6a\xc0\xba\x5d\x25\xed\x72\x8f\xc9\xbe\xf1\xba\xe2\x7c\xdf\xa7\x14\xec\x0b\xbb\x70\x22\x16\x19\x75\x96\xf1\x8a\x8c\x8c\xf5\x75\xa9\xea\xc6\xa6\xcb\x87\x75\x9b\x0d\x53\x24\x9c\xe6\xd8\x64\x56\x0f\x4e\xbe\xb0\x0b\xd7\xf3\x9f\xd5\x42\x92\x2f\xec\x62\x6b\xdd\xf0\xac\x63\xe1\xf0\x17\x76\xf1\xa7\xae\x19\x9e\xdd\x2e\x84\x9a\x35\x57\x0d\xcf\xb6\x97\x0d\xcf\x8a\x2f\x7d\x16\x0e\xdf\xd2\x85\x99\x3d\x88\x0f\x63\x6c\x46\xc6\xd4\xd9\xad\xca\x42\x37\x62\xf3\xc1\x03\xef\x8e\xb5\x9a\xb3\x2d\xc5\x9a\x5f\xd8\xc5\x7d\x94\xd3\xb9\x6a\xef\xa6\x15\x0a\x05\x95\xf2\x6c\x5f\x9c\x67\xef\x26\x5a\x85\x74\xc7\x36\x2b\xe6\x7e\x5d\x31\x9d\x41\xae\x5c\xed\xd7\x14\xd4\xe9\xd8\xac\xa8\x33\xc3\xe9\x7d\xcd\x9d\xd9\x8d\xaa\xea\x40\x6c\x55\xd9\x59\x37\x9d\x9d\x19\xa5\x9d\x95\x5a\x3b\x33\x6a\x3b\x33\x3f\xb7\x28\xee\xac\xae\xb9\xb3\x1b\x55\x77\x56\xe8\xee\xac\x50\xde\xfd\xdb\x29\xef\x7e\xb3\xf2\xee\x6f\x57\xde\xfd\xe2\xcb\xf7\xac\xfa\xb7\x7a\x25\x03\xc5\x9e\x0b\x2f\xfa\x0c\xc2\xa5\x5e\x8c\x7a\x2b\xaf\xb0\x87\x82\x12\x5e\x10\xe3\x65\x2f\x1e\xc8\x7a\xdf\x39\x25\x6b\xae\x85\xe8\xa3\xe4\x22\x41\x71\x5c\xd6\x5c\x7c\x15\x48\x10\x2e\x48\xc8\x3d\xe9\x9d\xfc\xa9\xeb\x22\x24\x72\xe5\x4f\x6c\x7f\xeb\x48\x5f\xe5\x84\x12\xdd\x66\x6e\x75\x3b\x66\xa8\x55\x63\x99\xba\xa5\xe7\x20\xbf\xf0\xc4\x7c\xb9\xe4\xa3\xe9\xac\xe5\x6e\x7d\x51\xa6\xf5\x41\xbf\xd1\x2d\x57\xef\x73\xea\xfe\x1a\x0f\x4f\x5c\xa5\x0a\x75\x4c\x68\x6b\x1f\xe9\xbc\xda\xb7\xb2\xec\x17\x1b\xa4\xf3\xba\xd0\xe6\x71\x0f\x54\xa8\xf9\x15\x0b\x41\xe8\x8a\x43\xbd\x4e\xb3\x42\x75\x19\xd3\x37\xf6\xe9\xbb\x06\xb0\x20\xab\x5e\xaf\x79\x3b\xb2\x1a\xde\x6f\xd0\x87\x2a\xfc\x8e\xb2\x69\xde\xd9\x51\xbc\x38\xad\xf3\x7b\x3c\xee\x2b\x9d\x06\x61\x03\x43\x4f\xfb\x4d\xb8\xdc\x3f\x0a\xde\x96\xaf\x40\x84\x6e\xe6\x2b\xea\x0e\xe9\xac\x83\x47\x6a\x7d\xae\x46\x97\xd4\xf1\xb3\xbe\xff\x4b\x15\x36\x9c\xbc\x4a\x85\xc5\xcb\x3f\x6f\x49\x97\x7e\x6f\xd4\x3d\x6a\xb9\x1e\xa8\xd4\x61\xfa\x50\xa5\x0e\xd3\x5e\x4a\x1d\xa6\xd5\x22\x31\x19\xf1\x98\x37\x70\x95\xc1\xce\x52\x3f\x70\x6f\xa7\x49\xd2\x4c\x3f\xc7\xd7\xd5\x4f\x6e\x5d\x09\x36\xbd\xc1\xf4\x6b\xfc\xb7\xb8\x97\x7a\x13\x40\xeb\x9a\xe6\x71\xac\x0f\x5a\xe4\xaf\x23\x96\x5e\xe9\x53\x97\xd9\xde\xf4\x85\xfa\x87\x7a\x57\x6c\x84\xad\x2f\xf8\x2a\x17\x6b\x96\xf1\x4e\xd5\xb7\xdb\xf0\x6f\xb7\x5f\x7b\x37\xda\xaf\x6a\x5d\x54\xf9\x0f\xf0\x9d\x0e\x0f\xfe\x2f\x00\x00\xff\xff\x2a\x50\xb6\x34\xe7\x78\x00\x00") + +func jsBundleJsBytes() ([]byte, error) { + return bindataRead( + _jsBundleJs, + "js/bundle.js", + ) +} + +func jsBundleJs() (*asset, error) { + bytes, err := jsBundleJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "js/bundle.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -161,8 +161,8 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "components/dashboard.js": componentsDashboardJs, "dashboard.html": dashboardHtml, + "js/bundle.js": jsBundleJs, } // AssetDir returns the file names below a certain @@ -205,10 +205,10 @@ type bintree struct { Children map[string]*bintree } var _bintree = &bintree{nil, map[string]*bintree{ - "components": &bintree{nil, map[string]*bintree{ - "dashboard.js": &bintree{componentsDashboardJs, map[string]*bintree{}}, - }}, "dashboard.html": &bintree{dashboardHtml, map[string]*bintree{}}, + "js": &bintree{nil, map[string]*bintree{ + "bundle.js": &bintree{jsBundleJs, map[string]*bintree{}}, + }}, }} // RestoreAsset restores an asset under the given directory diff --git a/dashboard/assets/components/dashboard.js b/dashboard/assets/components/dashboard.js deleted file mode 100644 index 956b5d4e79..0000000000 --- a/dashboard/assets/components/dashboard.js +++ /dev/null @@ -1,282 +0,0 @@ -Chart.defaults.global.legend = { enabled: false }; - -const Component = React.Component; -// const Component = Inferno.Component; - -// isNullOrUndefined returns true if a variable is null or undefined. -let isNullOrUndefined = variable => variable === null || typeof variable === 'undefined'; - -// MenuItem renders an item for a Menu component and the belonging submenu items, if there is any. -class MenuItem extends Component { - render = () =>
  • - - - {this.props.text} -
    - - { - // Render dropdown menu only if there are children. - isNullOrUndefined(this.props.children) ||
      - {React.Children.map(this.props.children, child =>
    • {child}
    • )} -
    - } -
  • ; -} - -// Menu renders a menu component. -let Menu = () => ; - -let Clearfix = () =>
    ; - -// SideBar renders a sidebar component. -let SideBar = () =>
    -
    - - - -
    -
    ; - -// TopNavigation renders a top navigation component. -let TopNavigation = () =>
    - -
    ; - -// Chart name is already in use. -// ChartComponent renders a chart component and updates it, when the related data changes. -class ChartComponent extends Component { - constructor(props) { - super(props); - this.state = {}; - } - - componentDidMount = () => this.state.chart = new Chart(this.data, { - type: this.props.type, - data: this.props.data, - }); - - render = () => { - if (!isNullOrUndefined(this.state.chart)) { - this.state.chart.update(); - } - - return ( -
    -
    -
    -

    {this.props.text}

    -
      -
    • - { - // Render dropdown menu only if there are children. - isNullOrUndefined(this.props.children) ||
    • - -
        - {React.Children.map(this.props.children, child =>
      • {child}
      • )} -
      -
    • - } -
    • -
    • -
    - -
    -
    - {/* The chart will be generated here after the component is mounted (this.componentDidMount). */} - { - this.data = data - }}/> -
    -
    -
    - ); - } -} - -// Row renders a row component of charts only if there is any chart. -class Row extends Component { - render = () => isNullOrUndefined(this.props.children) ||
    {this.props.children}
    ; -} - -// PageContent renders a component for the page content. -class PageContent extends Component { - render = () =>
    -
    -
    -
    -

    Go Ethereum Dashboard - Statistics -

    -
    -
    - - - - Settings 1 - Settings 2 - - - - -
    -
    ; -} - -// Footer renders a footer. -let Footer = () =>
    -
    - Gentelella - Bootstrap Admin Template by Colorlib -
    - -
    ; - -const MEMORY_SAMPLE_LIMIT = 200; // Maximum number of memory data samples. -const TRAFFIC_SAMPLE_LIMIT = 200; // Maximum number of traffic data samples. - -// Dashboard renders a full dashboard component, which makes connection with the server and waits for messages. -// When there is a message, correspondingly updates the page's content. -class Dashboard extends Component { - constructor(props) { - super(props); - this.state = { - charts: { - memory: { - labels: [], - datasets: [{ - label: "system/memory/inuse", - backgroundColor: "rgba(38, 185, 154, 0.31)", - borderColor: "rgba(38, 185, 154, 0.7)", - pointBorderColor: "rgba(38, 185, 154, 0.7)", - pointBackgroundColor: "rgba(38, 185, 154, 0.7)", - pointHoverBackgroundColor: "#fff", - pointHoverBorderColor: "rgba(220,220,220,1)", - pointBorderWidth: 1, - data: [], - }], - }, - traffic: { - labels: [], - datasets: [{ - label: "p2p/InboundTraffic", - backgroundColor: "rgba(3, 88, 106, 0.3)", - borderColor: "rgba(3, 88, 106, 0.70)", - pointBorderColor: "rgba(3, 88, 106, 0.70)", - pointBackgroundColor: "rgba(3, 88, 106, 0.70)", - pointHoverBackgroundColor: "#fff", - pointHoverBorderColor: "rgba(151,187,205,1)", - pointBorderWidth: 1, - data: [], - }], - }, - }, - }; - } - - updateCharts = msg => { - let memory = this.state.charts.memory; - let traffic = this.state.charts.traffic; - - // Fill the dashboard with the past data. metrics is set only in the first msg, after the connection is established. - if (msg.metrics !== undefined) { - // Clear the arrays to prevent data confusion with the previous connection. - memory.labels = []; - traffic.labels = []; - memory.datasets[0].data = []; - traffic.datasets[0].data = []; - - let mem = msg.metrics.memory; - let traff = msg.metrics.processor; // TODO (kurkomisi): !!! - - // Put the past data to the beginning of the arrays. This prevents confusion with the next msg data, which goes to the end. - for (let i = mem.length - 1; i >= 0 && MEMORY_SAMPLE_LIMIT > memory.labels.length; --i) { - memory.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); - traffic.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); - memory.datasets[0].data.unshift(mem[i].value); - traffic.datasets[0].data.unshift(traff[i].value); - } - - this.setState({charts: {memory: memory, traffic: traffic,}}); // Update the components. - return; - } - - // Put the new data to the end of the arrays. - if (msg.memory !== undefined) { - // Remove the first elements in case the samples' amount exceeds the limit. - if (memory.labels.length === MEMORY_SAMPLE_LIMIT) { - memory.labels.shift(); - traffic.labels.shift(); - memory.datasets[0].data.shift(); - traffic.datasets[0].data.shift(); - } - memory.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); - traffic.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); - memory.datasets[0].data.push(msg.memory.value); - traffic.datasets[0].data.push(msg.processor.value); - - this.setState({charts: {memory: memory, traffic: traffic,}}); // Update the components. - } - }; - - reconnect = () => { - let server = new WebSocket("ws://" + location.host + "/api"); - let that = this; - - server.onmessage = event => { - let msg = JSON.parse(event.data); - if (isNullOrUndefined(msg)) { - return; - } - that.updateCharts(msg); - }; - - server.onclose = () => setTimeout(that.reconnect, 3000); - }; - - componentDidMount = () => this.reconnect(); - - render = () =>
    -
    - - - -
    -
    -
    ; -} \ No newline at end of file diff --git a/dashboard/assets/dashboard.html b/dashboard/assets/dashboard.html index b14965d859..ee4c09a76b 100644 --- a/dashboard/assets/dashboard.html +++ b/dashboard/assets/dashboard.html @@ -12,37 +12,22 @@ - - - - - - + + - - + - - - - - + + + - - - - - - - - - + \ No newline at end of file diff --git a/dashboard/assets/js/bundle.js b/dashboard/assets/js/bundle.js new file mode 100644 index 0000000000..0e7dfb08e3 --- /dev/null +++ b/dashboard/assets/js/bundle.js @@ -0,0 +1,566 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { + +module.exports = Inferno; + +/***/ }), +/* 1 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return isNullOrUndefined; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return mapChildren; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Clearfix; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return MEMORY_SAMPLE_LIMIT; }); +/* unused harmony export TRAFFIC_SAMPLE_LIMIT */ +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_inferno__); +// isNullOrUndefined returns true if a variable is null or undefined. +var isNullOrUndefined = function isNullOrUndefined(variable) { + return variable === null || typeof variable === 'undefined'; +}; + +var mapChildren = function mapChildren(children, mapFunc) { + return !Array.isArray(children) || children.length < 1 || children.length === 1 ? mapFunc(children) : children.map(mapFunc); +}; + + +var Clearfix = function Clearfix() { + return Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "clearfix"); +}; + +var MEMORY_SAMPLE_LIMIT = 200; // Maximum number of memory data samples. +var TRAFFIC_SAMPLE_LIMIT = 200; // Maximum number of traffic data samples. + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +module.exports = Inferno.Component; + +/***/ }), +/* 3 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_inferno__); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_Dashboard__ = __webpack_require__(4); + + + +// Renders the whole dashboard. + +__WEBPACK_IMPORTED_MODULE_0_inferno___default.a.render(Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__components_Dashboard__["a" /* default */]), document.getElementById('dashboard')); + +/***/ }), +/* 4 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component__ = __webpack_require__(2); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_component__); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Common__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__SideBar__ = __webpack_require__(5); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__TopNavigation__ = __webpack_require__(6); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__PageContent__ = __webpack_require__(7); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__Footer__ = __webpack_require__(8); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_inferno__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6_inferno__); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + + + + + + + +// Dashboard is the main component, which renders the whole page, +// makes connection with the server and listens for messages. +// When there is an incoming message, updates the page's content correspondingly. + + +var Dashboard = function (_Component) { + _inherits(Dashboard, _Component); + + function Dashboard(props) { + _classCallCheck(this, Dashboard); + + var _this = _possibleConstructorReturn(this, _Component.call(this, props)); + + _this.updateCharts = function (msg) { + var memory = _this.state.charts.memory; + var traffic = _this.state.charts.traffic; + + // Fill the dashboard with the past data. metrics is set only in the first msg, + // after the connection is established. + if (msg.metrics !== undefined) { + // Clear the arrays to prevent data confusion with the previous connection. + memory.labels = []; + traffic.labels = []; + memory.datasets[0].data = []; + traffic.datasets[0].data = []; + + var mem = msg.metrics.memory; + var traff = msg.metrics.processor; // TODO (kurkomisi): !!! + + // Put the past data to the beginning of the arrays. This prevents confusion with the next msg data, + // which goes to the end. + for (var i = mem.length - 1; i >= 0 && __WEBPACK_IMPORTED_MODULE_1__Common__["b" /* MEMORY_SAMPLE_LIMIT */] > memory.labels.length; --i) { + memory.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); + traffic.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); + memory.datasets[0].data.unshift(mem[i].value); + traffic.datasets[0].data.unshift(traff[i].value); + } + + _this.setState({ charts: { memory: memory, traffic: traffic } }); // Update the components. + return; + } + + // Put the new data to the end of the arrays. + if (msg.memory !== undefined) { + // Remove the first elements in case the samples' amount exceeds the limit. + if (memory.labels.length === __WEBPACK_IMPORTED_MODULE_1__Common__["b" /* MEMORY_SAMPLE_LIMIT */]) { + memory.labels.shift(); + traffic.labels.shift(); + memory.datasets[0].data.shift(); + traffic.datasets[0].data.shift(); + } + memory.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); + traffic.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); + memory.datasets[0].data.push(msg.memory.value); + traffic.datasets[0].data.push(msg.processor.value); + + _this.setState({ charts: { memory: memory, traffic: traffic } }); // Update the components. + } + }; + + _this.reconnect = function () { + var server = new WebSocket("ws://" + location.host + "/api"); + + server.onmessage = function (event) { + var msg = JSON.parse(event.data); + if (Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(msg)) { + return; + } + _this.updateCharts(msg); + }; + + server.onclose = function () { + return setTimeout(_this.reconnect, 3000); + }; + }; + + _this.componentDidMount = function () { + return _this.reconnect(); + }; + + _this.render = function () { + return Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(2, "div", "container body", Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(2, "div", "main_container", [Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_2__SideBar__["a" /* SideBar */]), Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_3__TopNavigation__["a" /* TopNavigation */]), Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_4__PageContent__["a" /* default */], null, null, { + "charts": _this.state.charts + }), Object(__WEBPACK_IMPORTED_MODULE_6_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_5__Footer__["a" /* Footer */])])); + }; + + _this.state = { + charts: { // Stores the state of the charts. + memory: { + labels: [], + datasets: [{ + label: "system/memory/inuse", + backgroundColor: "rgba(38, 185, 154, 0.31)", + borderColor: "rgba(38, 185, 154, 0.7)", + pointBorderColor: "rgba(38, 185, 154, 0.7)", + pointBackgroundColor: "rgba(38, 185, 154, 0.7)", + pointHoverBackgroundColor: "#fff", + pointHoverBorderColor: "rgba(220,220,220,1)", + pointBorderWidth: 1, + data: [] + }] + }, + traffic: { + labels: [], + datasets: [{ + label: "p2p/InboundTraffic", + backgroundColor: "rgba(3, 88, 106, 0.3)", + borderColor: "rgba(3, 88, 106, 0.70)", + pointBorderColor: "rgba(3, 88, 106, 0.70)", + pointBackgroundColor: "rgba(3, 88, 106, 0.70)", + pointHoverBackgroundColor: "#fff", + pointHoverBorderColor: "rgba(151,187,205,1)", + pointBorderWidth: 1, + data: [] + }] + } + } + }; + return _this; + } + + // updateCharts Analyzes the incoming message, and updates the charts' content correspondingly. + + + // reconnect establishes a websocket connection with the server, listens for incoming messages + // and tries to reconnect on connection loss. + + + // componentDidMount initiates the establishment of the first websocket connection after the component is rendered. + + + // render renders the components of the dashboard. + + + return Dashboard; +}(__WEBPACK_IMPORTED_MODULE_0_component___default.a); + +/* harmony default export */ __webpack_exports__["a"] = (Dashboard); + +/***/ }), +/* 5 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return SideBar; }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component__ = __webpack_require__(2); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_component__); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Common__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_inferno__); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + + + +// MenuItem renders an item for a Menu component and the belonging submenu items, if there is any. + + +var MenuItem = function (_Component) { + _inherits(MenuItem, _Component); + + function MenuItem() { + var _temp, _this, _ret; + + _classCallCheck(this, MenuItem); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return _ret = (_temp = (_this = _possibleConstructorReturn(this, _Component.call.apply(_Component, [this].concat(args))), _this), _this.render = function () { + return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa " + _this.props.className), _this.props.text, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "fa fa-chevron-down")]), + // Render dropdown menu only if there are children. + Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this.props.children) || Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "nav child_menu", Object(__WEBPACK_IMPORTED_MODULE_1__Common__["d" /* mapChildren */])(_this.props.children, function (child) { + return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, child); + }))]); + }, _temp), _possibleConstructorReturn(_this, _ret); + } + + return MenuItem; +}(__WEBPACK_IMPORTED_MODULE_0_component___default.a); + +// Menu renders a menu component. + + +var Menu = function Menu() { + return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "nav side-menu", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, { + "className": "fa-home", + "text": "Home", + children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Dashboard1", { + "href": "dashboard1.html" + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Dashboard2", { + "href": "dashboard2.html" + })] + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, { + "className": "fa-edit", + "text": "Networking", + children: Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Networking", { + "href": "networking.html" + }) + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, { + "className": "fa-desktop", + "text": "Txpool", + children: Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Txpool", { + "href": "txpool.html" + }) + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, { + "className": "fa-table", + "text": "Logs", + children: Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Logs", { + "href": "logs.html" + }) + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, { + "className": "fa-clone", + "text": "Blockchain", + children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain1", { + "href": "blockchain1.html" + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain2", { + "href": "blockchain2.html" + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain3", { + "href": "blockchain3.html" + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain4", { + "href": "blockchain4.html" + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Blockchain5", { + "href": "blockchain5.html" + })] + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, MenuItem, null, null, { + "className": "fa-bar-chart-o", + "text": "System" + })]); +}; + +// SideBar renders a sidebar component. +var SideBar = function SideBar() { + return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "col-md-3 left_col", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "left_col scroll-view", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "navbar nav_title", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "site_title", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-paw"), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "span", null, "Go Ethereum Dashboard")], { + "href": "dashboard.html" + }), { + "style": { border: 0 } + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "main_menu_side hidden-print main_menu", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "menu_section", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, Menu)), { + "id": "sidebar-menu" + })])); +}; + +/***/ }), +/* 6 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TopNavigation; }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_inferno__); + +// TopNavigation renders a top navigation component. +var TopNavigation = function TopNavigation() { + return Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "top_nav", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "nav_menu", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "nav", "", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "div", "nav toggle", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "a", null, [" ", Object(__WEBPACK_IMPORTED_MODULE_0_inferno__["createVNode"])(2, "i", "fa fa-bars")], { + "id": "_______menu_toggle" + })), { + "role": "navigation" + }))); +}; + +/***/ }), +/* 7 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component__ = __webpack_require__(2); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_component___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_component__); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Common__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_inferno__); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + + + + +// Chart name is already in use. +// ChartComponent renders a chart component and updates it, when the related data changes. + + +var ChartComponent = function (_Component) { + _inherits(ChartComponent, _Component); + + function ChartComponent(props) { + _classCallCheck(this, ChartComponent); + + var _this = _possibleConstructorReturn(this, _Component.call(this, props)); + + _this.componentDidMount = function () { + return _this.state.chart = new Chart(_this.data, { + type: _this.props.type, + data: _this.props.data + }); + }; + + _this.render = function () { + if (!Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this.state.chart)) { + _this.state.chart.update(); + } + + return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", _this.props.className, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "x_panel", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "x_title", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "h2", null, _this.props.text), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "nav navbar-right panel_toolbox", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "collapse-link", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-chevron-up"))), + // Render dropdown menu only if there are children. + Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this.props.children) || Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", "dropdown", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "dropdown-toggle", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-wrench"), { + "href": "#", + "data-toggle": "dropdown", + "role": "button", + "aria-expanded": "false" + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "ul", "dropdown-menu", Object(__WEBPACK_IMPORTED_MODULE_1__Common__["d" /* mapChildren */])(_this.props.children, function (child) { + return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, child); + }), { + "role": "menu" + })]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "li", null, Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", "close-link", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "i", "fa fa-close")))]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */])]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "x_content", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "canvas", null, null, null, null, function (data) { + _this.data = data; + }))])); + }; + + _this.state = {}; + return _this; + } + + return ChartComponent; +}(__WEBPACK_IMPORTED_MODULE_0_component___default.a); + +// Row renders a row component of charts only if there is any chart. + + +var Row = function (_Component2) { + _inherits(Row, _Component2); + + function Row() { + var _temp, _this2, _ret; + + _classCallCheck(this, Row); + + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return _ret = (_temp = (_this2 = _possibleConstructorReturn(this, _Component2.call.apply(_Component2, [this].concat(args))), _this2), _this2.render = function () { + return Object(__WEBPACK_IMPORTED_MODULE_1__Common__["c" /* isNullOrUndefined */])(_this2.props.children) || Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "row", [" ", _this2.props.children, " "]); + }, _temp), _possibleConstructorReturn(_this2, _ret); + } + + return Row; +}(__WEBPACK_IMPORTED_MODULE_0_component___default.a); + +// PageContent renders a component for the page content. + + +var PageContent = function (_Component3) { + _inherits(PageContent, _Component3); + + function PageContent() { + var _temp2, _this3, _ret2; + + _classCallCheck(this, PageContent); + + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return _ret2 = (_temp2 = (_this3 = _possibleConstructorReturn(this, _Component3.call.apply(_Component3, [this].concat(args))), _this3), _this3.render = function () { + return Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "right_col", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "", [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "page-title", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "div", "title_left", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "h3", null, ["Go Ethereum Dashboard", Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "small", null, "Statistics")]))), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */]), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, Row, null, null, { + children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, ChartComponent, null, null, { + "className": "col-md-6 col-sm-6 col-xs-12", + "text": "Memory usage system/memory/inuse", + "type": "line", + "data": _this3.props.charts.memory, + children: [Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Settings 1", { + "href": "#" + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(2, "a", null, "Settings 2", { + "href": "#" + })] + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, ChartComponent, null, null, { + "className": "col-md-6 col-sm-6 col-xs-12", + "text": "Inbound traffic p2p/InboundTraffic", + "type": "line", + "data": _this3.props.charts.traffic + })] + }), Object(__WEBPACK_IMPORTED_MODULE_2_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_1__Common__["a" /* Clearfix */])]), { + "role": "main" + }); + }, _temp2), _possibleConstructorReturn(_this3, _ret2); + } + + return PageContent; +}(__WEBPACK_IMPORTED_MODULE_0_component___default.a); + +/* harmony default export */ __webpack_exports__["a"] = (PageContent); + +/***/ }), +/* 8 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Footer; }); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Common__ = __webpack_require__(1); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_inferno__ = __webpack_require__(0); +/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_inferno___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_inferno__); + + +// Footer renders a footer component. + +var Footer = function Footer() { + return Object(__WEBPACK_IMPORTED_MODULE_1_inferno__["createVNode"])(2, "footer", null, [Object(__WEBPACK_IMPORTED_MODULE_1_inferno__["createVNode"])(2, "div", "pull-right", "Copyright 2017 The go-ethereum Authors"), Object(__WEBPACK_IMPORTED_MODULE_1_inferno__["createVNode"])(16, __WEBPACK_IMPORTED_MODULE_0__Common__["a" /* Clearfix */])]); +}; + +/***/ }) +/******/ ]); \ No newline at end of file diff --git a/dashboard/bundler/package.json b/dashboard/bundler/package.json new file mode 100644 index 0000000000..eff112353e --- /dev/null +++ b/dashboard/bundler/package.json @@ -0,0 +1,38 @@ +{ + "jshintConfig": { + "esversion": 6 + }, + "eslintConfig": { + "parser": "babel-eslint" + }, + "babel": { + "presets": [ + ["es2015", {"loose": true, "modules": false}], + "stage-0" + ], + "plugins": [ + "babel-plugin-syntax-jsx", + ["babel-plugin-inferno", {"imports": true}] + ] + }, + "devDependencies": { + "babel-core": "^6.26.0", + "babel-eslint": "^7.2.3", + "babel-loader": "^7.1.2", + "babel-plugin-inferno": "^3.2.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-syntax-object-rest-spread": "^6.13.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-stage-0": "^6.24.1", + "eslint": "^4.5.0", + "eslint-config-inferno-app": "^4.2.0", + "eslint-loader": "^1.9.0", + "glob": "^7.1.2", + "inferno-devtools": "^3.8.2", + "path": "^0.12.7", + "url": "^0.11.0", + "webpack": "^3.5.5", + "webpack-dev-server": "^2.7.1" + } +} diff --git a/dashboard/bundler/src/components/Common.js b/dashboard/bundler/src/components/Common.js new file mode 100644 index 0000000000..788d88882e --- /dev/null +++ b/dashboard/bundler/src/components/Common.js @@ -0,0 +1,10 @@ +// isNullOrUndefined returns true if a variable is null or undefined. +export const isNullOrUndefined = variable => variable === null || typeof variable === 'undefined'; + +export const mapChildren = (children, mapFunc) => !Array.isArray(children) || children.length < 1 || +children.length === 1 ? mapFunc(children) : children.map(mapFunc); + +export const Clearfix = () =>
    ; + +export const MEMORY_SAMPLE_LIMIT = 200; // Maximum number of memory data samples. +export const TRAFFIC_SAMPLE_LIMIT = 200; // Maximum number of traffic data samples. \ No newline at end of file diff --git a/dashboard/bundler/src/components/Dashboard.js b/dashboard/bundler/src/components/Dashboard.js new file mode 100644 index 0000000000..886b6f55c4 --- /dev/null +++ b/dashboard/bundler/src/components/Dashboard.js @@ -0,0 +1,124 @@ +import Component from 'component'; +import {isNullOrUndefined, MEMORY_SAMPLE_LIMIT} from "./Common"; +import {SideBar} from "./SideBar"; +import {TopNavigation} from "./TopNavigation"; +import PageContent from './PageContent'; +import {Footer} from './Footer'; + +// Dashboard is the main component, which renders the whole page, +// makes connection with the server and listens for messages. +// When there is an incoming message, updates the page's content correspondingly. +export default class Dashboard extends Component { + constructor(props) { + super(props); + this.state = { + charts: { // Stores the state of the charts. + memory: { + labels: [], + datasets: [{ + label: "system/memory/inuse", + backgroundColor: "rgba(38, 185, 154, 0.31)", + borderColor: "rgba(38, 185, 154, 0.7)", + pointBorderColor: "rgba(38, 185, 154, 0.7)", + pointBackgroundColor: "rgba(38, 185, 154, 0.7)", + pointHoverBackgroundColor: "#fff", + pointHoverBorderColor: "rgba(220,220,220,1)", + pointBorderWidth: 1, + data: [], + }], + }, + traffic: { + labels: [], + datasets: [{ + label: "p2p/InboundTraffic", + backgroundColor: "rgba(3, 88, 106, 0.3)", + borderColor: "rgba(3, 88, 106, 0.70)", + pointBorderColor: "rgba(3, 88, 106, 0.70)", + pointBackgroundColor: "rgba(3, 88, 106, 0.70)", + pointHoverBackgroundColor: "#fff", + pointHoverBorderColor: "rgba(151,187,205,1)", + pointBorderWidth: 1, + data: [], + }], + }, + }, + }; + } + + // updateCharts Analyzes the incoming message, and updates the charts' content correspondingly. + updateCharts = msg => { + const memory = this.state.charts.memory; + const traffic = this.state.charts.traffic; + + // Fill the dashboard with the past data. metrics is set only in the first msg, + // after the connection is established. + if (msg.metrics !== undefined) { + // Clear the arrays to prevent data confusion with the previous connection. + memory.labels = []; + traffic.labels = []; + memory.datasets[0].data = []; + traffic.datasets[0].data = []; + + const mem = msg.metrics.memory; + const traff = msg.metrics.processor; // TODO (kurkomisi): !!! + + // Put the past data to the beginning of the arrays. This prevents confusion with the next msg data, + // which goes to the end. + for (let i = mem.length - 1; i >= 0 && MEMORY_SAMPLE_LIMIT > memory.labels.length; --i) { + memory.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); + traffic.labels.unshift(mem[i].time.substring(mem[i].time.length - 5)); + memory.datasets[0].data.unshift(mem[i].value); + traffic.datasets[0].data.unshift(traff[i].value); + } + + this.setState({charts: {memory, traffic,}}); // Update the components. + return; + } + + // Put the new data to the end of the arrays. + if (msg.memory !== undefined) { + // Remove the first elements in case the samples' amount exceeds the limit. + if (memory.labels.length === MEMORY_SAMPLE_LIMIT) { + memory.labels.shift(); + traffic.labels.shift(); + memory.datasets[0].data.shift(); + traffic.datasets[0].data.shift(); + } + memory.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); + traffic.labels.push(msg.memory.time.substring(msg.memory.time.length - 5)); + memory.datasets[0].data.push(msg.memory.value); + traffic.datasets[0].data.push(msg.processor.value); + + this.setState({charts: {memory, traffic,}}); // Update the components. + } + }; + + // reconnect establishes a websocket connection with the server, listens for incoming messages + // and tries to reconnect on connection loss. + reconnect = () => { + const server = new WebSocket("ws://" + location.host + "/api"); + + server.onmessage = event => { + const msg = JSON.parse(event.data); + if (isNullOrUndefined(msg)) { + return; + } + this.updateCharts(msg); + }; + + server.onclose = () => setTimeout(this.reconnect, 3000); + }; + + // componentDidMount initiates the establishment of the first websocket connection after the component is rendered. + componentDidMount = () => this.reconnect(); + + // render renders the components of the dashboard. + render = () =>
    +
    + + + +
    +
    +
    ; +} \ No newline at end of file diff --git a/dashboard/bundler/src/components/Footer.js b/dashboard/bundler/src/components/Footer.js new file mode 100644 index 0000000000..bdc13bff9c --- /dev/null +++ b/dashboard/bundler/src/components/Footer.js @@ -0,0 +1,9 @@ +import {Clearfix} from "./Common"; + +// Footer renders a footer component. +export const Footer = () =>
    +
    + Copyright 2017 The go-ethereum Authors +
    + +
    ; \ No newline at end of file diff --git a/dashboard/bundler/src/components/PageContent.js b/dashboard/bundler/src/components/PageContent.js new file mode 100644 index 0000000000..45f287936c --- /dev/null +++ b/dashboard/bundler/src/components/PageContent.js @@ -0,0 +1,83 @@ +import Component from 'component'; +import {isNullOrUndefined, mapChildren, Clearfix} from "./Common"; + +// Chart name is already in use. +// ChartComponent renders a chart component and updates it, when the related data changes. +class ChartComponent extends Component { + constructor(props) { + super(props); + this.state = {}; + } + + componentDidMount = () => this.state.chart = new Chart(this.data, { + type: this.props.type, + data: this.props.data, + }); + + render = () => { + if (!isNullOrUndefined(this.state.chart)) { + this.state.chart.update(); + } + + return
    +
    +
    +

    {this.props.text}

    +
      +
    • + { + // Render dropdown menu only if there are children. + isNullOrUndefined(this.props.children) ||
    • + +
        + {mapChildren(this.props.children, child =>
      • {child}
      • )} +
      +
    • + } +
    • +
    • +
    + +
    +
    + {/* The chart will be generated here after the component is mounted (this.componentDidMount). */} + { + this.data = data + }}/> +
    +
    +
    ; + } +} + +// Row renders a row component of charts only if there is any chart. +class Row extends Component { + render = () => isNullOrUndefined(this.props.children) ||
    {this.props.children}
    ; +} + +// PageContent renders a component for the page content. +export default class PageContent extends Component { + render = () =>
    +
    +
    +
    +

    Go Ethereum Dashboard + Statistics +

    +
    +
    + + + + Settings 1 + Settings 2 + + + + +
    +
    ; +} \ No newline at end of file diff --git a/dashboard/bundler/src/components/SideBar.js b/dashboard/bundler/src/components/SideBar.js new file mode 100644 index 0000000000..f1f7313bdd --- /dev/null +++ b/dashboard/bundler/src/components/SideBar.js @@ -0,0 +1,60 @@ +import Component from 'component'; +import {isNullOrUndefined, mapChildren, Clearfix} from "./Common"; + +// MenuItem renders an item for a Menu component and the belonging submenu items, if there is any. +class MenuItem extends Component { + render = () =>
  • + + + {this.props.text} +
    + + { + // Render dropdown menu only if there are children. + isNullOrUndefined(this.props.children) ||
      + {mapChildren(this.props.children, child =>
    • {child}
    • )} +
    + } +
  • ; +} + +// Menu renders a menu component. +const Menu = () => ; + +// SideBar renders a sidebar component. +export const SideBar = () =>
    +
    + + + +
    +
    ; \ No newline at end of file diff --git a/dashboard/bundler/src/components/TopNavigation.js b/dashboard/bundler/src/components/TopNavigation.js new file mode 100644 index 0000000000..e8a31226dc --- /dev/null +++ b/dashboard/bundler/src/components/TopNavigation.js @@ -0,0 +1,12 @@ +// TopNavigation renders a top navigation component. +export const TopNavigation = () => ; \ No newline at end of file diff --git a/dashboard/bundler/src/index.js b/dashboard/bundler/src/index.js new file mode 100644 index 0000000000..4f3206b746 --- /dev/null +++ b/dashboard/bundler/src/index.js @@ -0,0 +1,5 @@ +import Inferno from 'inferno'; +import Dashboard from './components/Dashboard'; + +// Renders the whole dashboard. +Inferno.render(, document.getElementById('dashboard')); \ No newline at end of file diff --git a/dashboard/bundler/webpack.config.js b/dashboard/bundler/webpack.config.js new file mode 100644 index 0000000000..7677b7fd29 --- /dev/null +++ b/dashboard/bundler/webpack.config.js @@ -0,0 +1,21 @@ +const path = require('path'); + +module.exports = { + entry: './src/index.js', + output: { + path: path.resolve(__dirname, '../assets/js'), + filename: 'bundle.js', + }, + externals: { // External libraries, which will not be included in the bundled file(s). + inferno: 'Inferno', + component: 'Inferno.Component', + }, + module: { + loaders: [ + { + test: /\.js$/, // regexp for JS files + loader: 'babel-loader', // The babel configuration is in the package.json. + }, + ], + }, +}; \ No newline at end of file diff --git a/dashboard/dashboard.go b/dashboard/dashboard.go index 989495b592..17595c99b9 100644 --- a/dashboard/dashboard.go +++ b/dashboard/dashboard.go @@ -81,7 +81,7 @@ type status struct { // New creates a new dashboard instance with the given configuration. func New(config *Config) (*dashboard, error) { return &dashboard{ - conns: make(map[uint32]*client), + conns: make(map[uint32]*client), config: config, Metrics: &metricSamples{}, closing: make(chan chan error),