Compare commits
93 commits
pr-infobox
...
master
Author | SHA1 | Date | |
---|---|---|---|
d70d3d7c5f | |||
fe17e32886 | |||
420be21fa5 | |||
a971dcfeed | |||
0a05523dd1 | |||
f416c33498 | |||
93e0a9c758 | |||
d231cbd5bc | |||
11368e60de | |||
587740af80 | |||
1235d8cb46 | |||
100268f3b7 | |||
35589eabef | |||
8f7c63bbce | |||
b2c2627d73 | |||
a7756c44e8 | |||
5f98da2717 | |||
1a6a4329b5 | |||
ccc5f0f526 | |||
5cb88e8d06 | |||
44bb8e9d3d | |||
7e6d054e98 | |||
94662cb3dc | |||
2ca2604403 | |||
0171ebe7e1 | |||
9e7049c9e3 | |||
7d145141c1 | |||
5a5ce1d346 | |||
224240c1c4 | |||
7eb0675be0 | |||
1641bc2437 | |||
84aee48229 | |||
e9711c6efc | |||
5599be5cd6 | |||
808b8c1986 | |||
00a8e5117d | |||
cb065d8d07 | |||
bf2e858c24 | |||
46de672dc9 | |||
07d5e3f636 | |||
cfd778dadb | |||
8f7b1e15ce | |||
760cca6806 | |||
2844a203d5 | |||
1b332508a0 | |||
8435885e5c | |||
f80ea1ca8f | |||
2bbdd1e077 | |||
2da97bca16 | |||
4c6ba69dd3 | |||
93355f28ba | |||
1bcd2b797d | |||
3971c36250 | |||
9719385076 | |||
20f0f08de3 | |||
81379a8263 | |||
31d1464805 | |||
e1e510c308 | |||
ce8853c0fa | |||
075076a2fe | |||
2f28c51da1 | |||
2a78af4208 | |||
8497a5f833 | |||
51c1c57e48 | |||
bc7e7888a5 | |||
31545c758d | |||
5fce722f24 | |||
08e00d4b91 | |||
1f07f50530 | |||
40dd7d2a0a | |||
54801ded8f | |||
961a18fec4 | |||
78de449b88 | |||
f8701ca0e3 | |||
fc2a1e3872 | |||
d1e28192b1 | |||
8d22417789 | |||
11a157c58a | |||
2eace51bfe | |||
a7c18b3a0a | |||
2e414cd5b8 | |||
8d4cc87994 | |||
f7bc41dcec | |||
309878d607 | |||
4c8b2c03c6 | |||
593a96e07e | |||
24987f4963 | |||
23ded77089 | |||
c541f5b721 | |||
2e823a90c8 | |||
97a00b6925 | |||
9f48cb1f3b | |||
273d0d5d23 |
16
.editorconfig
Normal file
16
.editorconfig
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
# Get rid of whitespace to avoid diffs with a bunch of EOL changes
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{js,html,scss,json}]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
10
.travis.yml
10
.travis.yml
|
@ -1,7 +1,7 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
before_install:
|
node_js:
|
||||||
- gem install sass
|
- "lts/*"
|
||||||
- npm install -g grunt-cli
|
- "node"
|
||||||
install:
|
install:
|
||||||
- npm install
|
- yarn
|
||||||
script: grunt
|
script: node_modules/.bin/grunt
|
||||||
|
|
13
Gemfile.lock
13
Gemfile.lock
|
@ -1,13 +0,0 @@
|
||||||
GEM
|
|
||||||
remote: https://rubygems.org/
|
|
||||||
specs:
|
|
||||||
sass (3.4.16)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
ruby
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
sass
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
1.10.6
|
|
|
@ -17,7 +17,7 @@ module.exports = function (grunt) {
|
||||||
|
|
||||||
grunt.loadTasks("tasks")
|
grunt.loadTasks("tasks")
|
||||||
|
|
||||||
grunt.registerTask("default", ["bower-install-simple", "lint", "saveRevision", "copy", "sass", "requirejs"])
|
grunt.registerTask("default", ["lint", "saveRevision", "copy", "sass", "postcss", "requirejs"])
|
||||||
grunt.registerTask("lint", ["eslint"])
|
grunt.registerTask("lint", ["eslint"])
|
||||||
grunt.registerTask("dev", ["default", "connect:server", "watch"])
|
grunt.registerTask("dev", ["default", "connect:server", "watch"])
|
||||||
}
|
}
|
||||||
|
|
72
README.md
72
README.md
|
@ -1,8 +1,8 @@
|
||||||
[![Build Status](https://travis-ci.org/plumpudding/hopglass.svg?branch=master)](https://travis-ci.org/plumpudding/hopglass)
|
[![Build Status](https://travis-ci.org/hopglass/hopglass.svg?branch=master)](https://travis-ci.org/hopglass/hopglass)
|
||||||
|
|
||||||
# HopGlass
|
# HopGlass
|
||||||
|
|
||||||
HopGlass is a frontend for the [HopGlass Server](https://github.com/plumpudding/hopglass-server).
|
HopGlass is a frontend for the [HopGlass Server](https://github.com/hopglass/hopglass-server).
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
|
||||||
|
@ -14,31 +14,32 @@ HopGlass is a frontend for the [HopGlass Server](https://github.com/plumpudding/
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
- npm
|
- NodeJS
|
||||||
- bower
|
- yarn (recommended) or npm
|
||||||
- grunt-cli
|
|
||||||
- Sass (>= 3.2)
|
|
||||||
|
|
||||||
# Installing dependencies
|
# Installing dependencies
|
||||||
|
|
||||||
Install npm and Sass with your package-manager. On Debian-like systems run:
|
Install npm package-manager. On Debian-like systems run:
|
||||||
|
|
||||||
sudo apt-get install npm ruby-sass
|
sudo apt-get install nodejs
|
||||||
|
|
||||||
or if you have bundler you can install ruby-sass simply via `bundle install`
|
**Note:** The official Debian packages for NodeJS are quite old, you might want to check at [NodeSource](https://github.com/nodesource/distributions/blob/master/README.md) for current binaries installable with your distribution's package manager.
|
||||||
|
|
||||||
On Mac you have to install only npm via brew and sass
|
On Mac you can install nodejs and yarn via brew:
|
||||||
|
|
||||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||||
brew install node
|
brew install node
|
||||||
sudo gem install sass
|
brew install yarn
|
||||||
|
|
||||||
|
On Arch Linux you can install nodejs and yarn via pacman:
|
||||||
|
|
||||||
|
sudo pacman -S nodejs yarn
|
||||||
|
|
||||||
Execute these commands on your server as a normal user to prepare the dependencies:
|
Execute these commands on your server as a normal user to prepare the dependencies:
|
||||||
|
|
||||||
git clone https://github.com/plumpudding/hopglass
|
git clone https://github.com/hopglass/hopglass
|
||||||
cd hopglass
|
cd hopglass
|
||||||
npm install
|
yarn install # or `npm install`
|
||||||
npm install grunt-cli
|
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ Copy `config.json.example` to `build/config.json` and change it to match your co
|
||||||
|
|
||||||
## dataPath (string/array)
|
## dataPath (string/array)
|
||||||
|
|
||||||
`dataPath` can be either a string containing the address of a [HopGlass Server](https://github.com/plumpudding/hopglass-server) or an array containing multiple addresses.
|
`dataPath` can be either a string containing the address of a [HopGlass Server](https://github.com/hopglass/hopglass-server) or an array containing multiple addresses.
|
||||||
Don't forget the trailing slash!
|
Don't forget the trailing slash!
|
||||||
Also, proxying the data through a webserver will allow GZip and thus will greatly reduce bandwidth consumption.
|
Also, proxying the data through a webserver will allow GZip and thus will greatly reduce bandwidth consumption.
|
||||||
It may help with firewall problems too.
|
It may help with firewall problems too.
|
||||||
|
@ -84,9 +85,25 @@ property and optionally `url` and `config` properties. If no `url` is supplied
|
||||||
`name` is assumed to name a
|
`name` is assumed to name a
|
||||||
[Leaflet-provider](http://leaflet-extras.github.io/leaflet-providers/preview/).
|
[Leaflet-provider](http://leaflet-extras.github.io/leaflet-providers/preview/).
|
||||||
|
|
||||||
|
## fixedCenter (array, optional)
|
||||||
|
|
||||||
|
This option allows to fix the map at one specific coordinate depending on following case-sensitive parameters:
|
||||||
|
|
||||||
|
- `lat` latitude of the center point
|
||||||
|
- `lng` longitude of the center point
|
||||||
|
- `radius` visible radius around the center in km
|
||||||
|
|
||||||
|
Examples for `fixedCenter`:
|
||||||
|
|
||||||
|
"fixedCenter": {
|
||||||
|
"lat": 50.80,
|
||||||
|
"lng": 12.07,
|
||||||
|
"radius": 30
|
||||||
|
}
|
||||||
|
|
||||||
## nodeInfos (array, optional)
|
## nodeInfos (array, optional)
|
||||||
|
|
||||||
This option allows to show client statistics depending on following case-sensitive parameters:
|
This option allows to show node statistics depending on following case-sensitive parameters:
|
||||||
|
|
||||||
- `name` caption of statistics segment in infobox
|
- `name` caption of statistics segment in infobox
|
||||||
- `href` absolute or relative URL to statistics image
|
- `href` absolute or relative URL to statistics image
|
||||||
|
@ -103,12 +120,12 @@ Examples for `nodeInfos`:
|
||||||
|
|
||||||
{ "name": "Clientstatistik",
|
{ "name": "Clientstatistik",
|
||||||
"href": "stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
"href": "stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
"thumbnail": "stats/render/dashboard-solo/db/node-byid?panelId=1&fullscreen&theme=light&width=600&height=300&var-nodeid={NODE_ID}"
|
"thumbnail": "stats/render/dashboard-solo/db/node-byid?panelId=1&fullscreen&theme=light&width=600&height=300&var-nodeid={NODE_ID}",
|
||||||
"caption": "Knoten {NODE_ID}"
|
"caption": "Knoten {NODE_ID}"
|
||||||
},
|
},
|
||||||
{ "name": "Uptime",
|
{ "name": "Uptime",
|
||||||
"href": "stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
"href": "stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
"thumbnail": "stats/render/dashboard-solo/db/node-byid?panelId=2&fullscreen&theme=light&width=600&height=300&var-nodeid={NODE_ID}"
|
"thumbnail": "stats/render/dashboard-solo/db/node-byid?panelId=2&fullscreen&theme=light&width=600&height=300&var-nodeid={NODE_ID}",
|
||||||
"caption": "Knoten {NODE_ID}"
|
"caption": "Knoten {NODE_ID}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -137,6 +154,25 @@ Examples for `globalInfos` using Grafana server rendering:
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
## linkInfos (array, optional)
|
||||||
|
|
||||||
|
This option allows to show link statistics depending on the following case-sensitive parameters:
|
||||||
|
|
||||||
|
- `name` caption of statistics segment in infobox
|
||||||
|
- `href` absolute or relative URL to statistics image
|
||||||
|
- `thumbnail` absolute or relative URL to thumbnail image,
|
||||||
|
can be the same like `href`
|
||||||
|
- `caption` is shown, if `thumbnail` is not present (no thumbnail in infobox)
|
||||||
|
|
||||||
|
To insert the source or target node-id in either `href`, `thumbnail` or `caption`
|
||||||
|
you can use the case-sensitive template strings `{SOURCE}` and `{TARGET}`.
|
||||||
|
|
||||||
|
"linkInfos": [
|
||||||
|
{ "href": "stats/dashboard/db/links?var-source={SOURCE}&var-target={TARGET}",
|
||||||
|
"thumbnail": "stats/render/dashboard-solo/db/links?panelId=1&fullscreen&theme=light&width=800&height=600&var-source={SOURCE}&var-target={TARGET}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
## siteNames (array, optional)
|
## siteNames (array, optional)
|
||||||
|
|
||||||
In this array name definitions for site statistics and node info can be saved. This requires one object for each site code. This object must contain:
|
In this array name definitions for site statistics and node info can be saved. This requires one object for each site code. This object must contain:
|
||||||
|
|
26
app.js
26
app.js
|
@ -1,20 +1,20 @@
|
||||||
require.config({
|
require.config({
|
||||||
baseUrl: "lib",
|
baseUrl: "lib",
|
||||||
paths: {
|
paths: {
|
||||||
"leaflet": "../bower_components/leaflet/dist/leaflet",
|
"leaflet": "../node_modules/leaflet/dist/leaflet",
|
||||||
"leaflet.label": "../bower_components/Leaflet.label/dist/leaflet.label",
|
"leaflet.label": "../node_modules/leaflet-label/dist/leaflet.label",
|
||||||
"leaflet.providers": "../bower_components/leaflet-providers/leaflet-providers",
|
"leaflet.providers": "../node_modules/leaflet-providers/leaflet-providers",
|
||||||
"chroma-js": "../bower_components/chroma-js/chroma.min",
|
"chroma-js": "../node_modules/chroma-js/chroma.min",
|
||||||
"moment": "../bower_components/moment/min/moment-with-locales.min",
|
"moment": "../node_modules/moment/min/moment-with-locales.min",
|
||||||
"tablesort": "../bower_components/tablesort/tablesort.min",
|
"tablesort": "../node_modules/tablesort/tablesort.min",
|
||||||
"tablesort.numeric": "../bower_components/tablesort/src/sorts/tablesort.numeric",
|
"tablesort.numeric": "../node_modules/tablesort/src/sorts/tablesort.numeric",
|
||||||
"d3": "../bower_components/d3/d3.min",
|
"d3": "../node_modules/d3/d3.min",
|
||||||
"numeral": "../bower_components/numeraljs/min/numeral.min",
|
"numeral": "../node_modules/numeraljs/min/numeral.min",
|
||||||
"numeral-intl": "../bower_components/numeraljs/min/languages.min",
|
"numeral-intl": "../node_modules/numeraljs/min/languages.min",
|
||||||
"virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom",
|
"virtual-dom": "../node_modules/virtual-dom/dist/virtual-dom",
|
||||||
"rbush": "../bower_components/rbush/rbush",
|
"rbush": "../node_modules/rbush/rbush",
|
||||||
"helper": "../helper",
|
"helper": "../helper",
|
||||||
"jshashes": "../bower_components/jshashes/hashes"
|
"jshashes": "../node_modules/jshashes/hashes"
|
||||||
},
|
},
|
||||||
shim: {
|
shim: {
|
||||||
"leaflet.label": ["leaflet"],
|
"leaflet.label": ["leaflet"],
|
||||||
|
|
36
bower.json
36
bower.json
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
"name": "HopGlass",
|
|
||||||
"ignore": [
|
|
||||||
"node_modules",
|
|
||||||
"bower_components",
|
|
||||||
"**/.*",
|
|
||||||
"test",
|
|
||||||
"tests"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"Leaflet.label": "~0.2.1",
|
|
||||||
"chroma-js": "~0.6.1",
|
|
||||||
"leaflet": "~0.7.3",
|
|
||||||
"ionicons": "~2.0.1",
|
|
||||||
"moment": "~2.9.0",
|
|
||||||
"requirejs": "~2.1.16",
|
|
||||||
"tablesort": "https://github.com/tristen/tablesort.git#v3.0.2",
|
|
||||||
"roboto-slab-fontface": "*",
|
|
||||||
"es6-shim": "~0.27.1",
|
|
||||||
"almond": "~0.3.1",
|
|
||||||
"r.js": "~2.1.16",
|
|
||||||
"d3": "~3.5.5",
|
|
||||||
"numeraljs": "~1.5.3",
|
|
||||||
"roboto-fontface": "~0.3.0",
|
|
||||||
"virtual-dom": "~2.0.1",
|
|
||||||
"leaflet-providers": "~1.0.27",
|
|
||||||
"rbush": "https://github.com/mourner/rbush.git#~1.3.5",
|
|
||||||
"jshashes": "~1.0.5"
|
|
||||||
},
|
|
||||||
"authors": [
|
|
||||||
"Milan Pässler <me@petabyteboy.de>",
|
|
||||||
"Nils Schneider <nils@nilsschneider.net>"
|
|
||||||
],
|
|
||||||
"license": "AGPL3",
|
|
||||||
"private": true
|
|
||||||
}
|
|
2
build.js
2
build.js
|
@ -1,6 +1,6 @@
|
||||||
({
|
({
|
||||||
baseUrl: "lib",
|
baseUrl: "lib",
|
||||||
name: "../bower_components/almond/almond",
|
name: "../node_modules/almond/almond",
|
||||||
mainConfigFile: "app.js",
|
mainConfigFile: "app.js",
|
||||||
include: "../app",
|
include: "../app",
|
||||||
wrap: true,
|
wrap: true,
|
||||||
|
|
|
@ -1,27 +1,62 @@
|
||||||
{
|
{
|
||||||
"dataPath": "https://map.luebeck.freifunk.net/data/",
|
"dataPath": "https://map.ffdus.de/data/",
|
||||||
"siteName": "Freifunk Lübeck",
|
"siteName": "Freifunk Flingern",
|
||||||
"mapSigmaScale": 0.5,
|
"mapSigmaScale": 0.5,
|
||||||
"showContact": true,
|
"showContact": true,
|
||||||
"maxAge": 14,
|
"maxAge": 14,
|
||||||
"mapLayers": [
|
"mapLayers": [
|
||||||
{ "name": "MapQuest",
|
{ "name": "CartoDB",
|
||||||
"url": "https://otile{s}-s.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg",
|
"url": "https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png",
|
||||||
"config": {
|
"config": {
|
||||||
"subdomains": "1234",
|
"maxZoom": 18,
|
||||||
"type": "osm",
|
"attribution": "© <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>, © | <a href=\"https://carto.com/attribution\">CARTO</a>"
|
||||||
"attribution": "Tiles © <a href=\"https://www.mapquest.com/\" target=\"_blank\">MapQuest</a>, Data CC-BY-SA OpenStreetMap",
|
|
||||||
"maxZoom": 18
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Stamen.TonerLite"
|
"name": "OpenStreetMap.HOT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Luftbilder NRW",
|
||||||
|
"url": "https://www.wms.nrw.de/geobasis/wms_nw_dop?",
|
||||||
|
"config": {
|
||||||
|
"maxZoom": 20,
|
||||||
|
"attribution": "<a href=\"http://www.bezreg-koeln.nrw.de/brk_internet/geobasis/luftbilderzeugnisse/digitale_orthophotos/index.html\">DOP20</a>, Land NRW (2017), Datenlizenz Deutschland - Namensnennung - Version 2.0 (<a href=\"https://www.govdata.de/dl-de/by-2-0\">www.govdata.de/dl-de/by-2-0</a>)",
|
||||||
|
"format": "image/jpeg",
|
||||||
|
"layers": "nw_dop_rgb"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nodeInfos": [
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/node-byid?panelId=1&theme=light&width=600&height=300&var-nodeid={NODE_ID}"
|
||||||
|
},
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/node-byid?panelId=2&theme=light&width=600&height=500&var-nodeid={NODE_ID}"
|
||||||
|
},
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/node-byid?var-nodeid={NODE_ID}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/node-byid?panelId=3&theme=light&width=600&height=200&var-nodeid={NODE_ID}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globalInfos": [
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/global?var-job=dus",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/global?panelId=1&&theme=light&width=800&height=600&var-job=dus"
|
||||||
|
},
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/global?var-job=dus",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/global?panelId=8&&theme=light&width=800&height=600&var-job=dus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"linkInfos": [
|
||||||
|
{ "href": "https://map.eulenfunk.de/stats/dashboard/db/links?var-source={SOURCE}&var-target={TARGET}",
|
||||||
|
"thumbnail": "https://map.eulenfunk.de/stats/render/dashboard-solo/db/links?panelId=1&&theme=light&width=800&height=600&var-source={SOURCE}&var-target={TARGET}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"siteNames": [
|
"siteNames": [
|
||||||
{ "site": "ffhl", "name": "Lübeck" },
|
{ "site": "dus", "name": "Flingern" }
|
||||||
{ "site": "ffeh", "name": "Entenhausen" },
|
],
|
||||||
{ "site": "ffgt", "name": "Gothamcity" },
|
"hwImg": [
|
||||||
{ "site": "ffal", "name": "Atlantis" }
|
{ "thumbnail": "https://cdn.rawgit.com/Moorviper/meshviewer_hwpics/master/nodes/{MODELHASH}.svg",
|
||||||
|
"caption": "Knoten {MODELHASH}"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
106
helper.js
106
helper.js
|
@ -1,23 +1,23 @@
|
||||||
function get(url) {
|
function get(url) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var req = new XMLHttpRequest();
|
var req = new XMLHttpRequest()
|
||||||
req.open('GET', url);
|
req.open('GET', url)
|
||||||
|
|
||||||
req.onload = function() {
|
req.onload = function() {
|
||||||
if (req.status == 200) {
|
if (req.status == 200) {
|
||||||
resolve(req.response);
|
resolve(req.response)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
reject(Error(req.statusText));
|
reject(Error(req.statusText))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
req.onerror = function() {
|
req.onerror = function() {
|
||||||
reject(Error("Network Error"));
|
reject(Error("Network Error"))
|
||||||
};
|
}
|
||||||
|
|
||||||
req.send();
|
req.send()
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getJSON(url) {
|
function getJSON(url) {
|
||||||
|
@ -73,6 +73,14 @@ function localStorageTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function listReplace(s, subst) {
|
||||||
|
for (key in subst) {
|
||||||
|
var re = new RegExp(key, 'g')
|
||||||
|
s = s.replace(re, subst[key])
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
/* Helpers working with nodes */
|
/* Helpers working with nodes */
|
||||||
|
|
||||||
function offline(d) {
|
function offline(d) {
|
||||||
|
@ -122,7 +130,13 @@ function attributeEntry(el, label, value) {
|
||||||
|
|
||||||
var tr = document.createElement("tr")
|
var tr = document.createElement("tr")
|
||||||
var th = document.createElement("th")
|
var th = document.createElement("th")
|
||||||
|
if (typeof label === "string")
|
||||||
th.textContent = label
|
th.textContent = label
|
||||||
|
else {
|
||||||
|
th.appendChild(label)
|
||||||
|
tr.className = "routerpic"
|
||||||
|
}
|
||||||
|
|
||||||
tr.appendChild(th)
|
tr.appendChild(th)
|
||||||
|
|
||||||
var td = document.createElement("td")
|
var td = document.createElement("td")
|
||||||
|
@ -138,3 +152,77 @@ function attributeEntry(el, label, value) {
|
||||||
|
|
||||||
return td
|
return td
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createIframe(opt, width, height) {
|
||||||
|
el = document.createElement("iframe")
|
||||||
|
width = typeof width !== 'undefined' ? width : '100%'
|
||||||
|
height = typeof height !== 'undefined' ? height : '350px'
|
||||||
|
|
||||||
|
if (opt.src)
|
||||||
|
el.src = opt.src
|
||||||
|
else
|
||||||
|
el.src = opt
|
||||||
|
|
||||||
|
if (opt.frameBorder)
|
||||||
|
el.frameBorder = opt.frameBorder
|
||||||
|
else
|
||||||
|
el.frameBorder = 1
|
||||||
|
|
||||||
|
if (opt.width)
|
||||||
|
el.width = opt.width
|
||||||
|
else
|
||||||
|
el.width = width
|
||||||
|
|
||||||
|
if (opt.height)
|
||||||
|
el.height = opt.height
|
||||||
|
else
|
||||||
|
el.height = height
|
||||||
|
|
||||||
|
el.scrolling = "no"
|
||||||
|
el.seamless = "seamless"
|
||||||
|
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
function showStat(o, subst) {
|
||||||
|
var content, caption
|
||||||
|
subst = typeof subst !== 'undefined' ? subst : {}
|
||||||
|
|
||||||
|
if (o.thumbnail) {
|
||||||
|
content = document.createElement("img")
|
||||||
|
content.src = listReplace(o.thumbnail, subst)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.caption) {
|
||||||
|
caption = listReplace(o.caption, subst)
|
||||||
|
|
||||||
|
if (!content)
|
||||||
|
content = document.createTextNode(caption)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.iframe) {
|
||||||
|
content = createIframe(o.iframe, o.width, o.height)
|
||||||
|
if (o.iframe.src)
|
||||||
|
content.src = listReplace(o.iframe.src, subst)
|
||||||
|
else
|
||||||
|
content.src = listReplace(o.iframe, subst)
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = document.createElement("p")
|
||||||
|
|
||||||
|
if (o.href) {
|
||||||
|
var link = document.createElement("a")
|
||||||
|
link.target = "_blank"
|
||||||
|
link.href = listReplace(o.href, subst)
|
||||||
|
link.appendChild(content)
|
||||||
|
|
||||||
|
if (caption && o.thumbnail)
|
||||||
|
link.title = caption
|
||||||
|
|
||||||
|
p.appendChild(link)
|
||||||
|
} else
|
||||||
|
p.appendChild(content)
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||||
<link rel="stylesheet" href="css/ionicons.min.css">
|
<link rel="stylesheet" href="css/ionicons.min.css">
|
||||||
<link rel="stylesheet" href="roboto-slab-fontface.css">
|
<link rel="stylesheet" href="css/roboto-slab/roboto-slab-fontface.css">
|
||||||
<link rel="stylesheet" href="roboto-fontface.css">
|
<link rel="stylesheet" href="css/roboto/roboto-fontface.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script src="vendor/es6-shim/es6-shim.min.js"></script>
|
<script src="vendor/es6-shim/es6-shim.min.js"></script>
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
|
@ -14,5 +14,15 @@
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="loader">
|
||||||
|
<p>
|
||||||
|
Lade<br />
|
||||||
|
<span class="spinner"></span><br />
|
||||||
|
Karte & Knoten...
|
||||||
|
</p>
|
||||||
|
<noscript>
|
||||||
|
<strong>JavaScript required</strong>
|
||||||
|
</noscript>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
14
index.html
14
index.html
|
@ -3,14 +3,14 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||||
<link rel="stylesheet" href="bower_components/roboto-slab-fontface/roboto-slab-fontface.css">
|
<link rel="stylesheet" href="node_modules/roboto-fontface/css/roboto-slab/roboto-slab-fontface.css">
|
||||||
<link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css">
|
<link rel="stylesheet" href="node_modules/roboto-fontface/css/roboto/roboto-fontface.css">
|
||||||
<link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css">
|
<link rel="stylesheet" href="node_modules/leaflet/dist/leaflet.css">
|
||||||
<link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css">
|
<link rel="stylesheet" href="node_modules/leaflet-label/dist/leaflet.label.css">
|
||||||
<link rel="stylesheet" href="bower_components/ionicons/css/ionicons.min.css">
|
<link rel="stylesheet" href="node_modules/ionicons/css/ionicons.min.css">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script src="bower_components/es6-shim/es6-shim.min.js"></script>
|
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
|
||||||
<script src="bower_components/requirejs/require.js" data-main="app"></script>
|
<script src="node_modules/requirejs/require.js" data-main="app"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -29,8 +29,8 @@ define(function () {
|
||||||
s += "https://www.gnu.org/licenses/</a>.</p>"
|
s += "https://www.gnu.org/licenses/</a>.</p>"
|
||||||
|
|
||||||
s += "<p>The source code is available at "
|
s += "<p>The source code is available at "
|
||||||
s += "<a href=\"https://github.com/plumpudding/hopglass\">"
|
s += "<a href=\"https://github.com/hopglass/hopglass\">"
|
||||||
s += "https://github.com/plumpudding/hopglass</a>."
|
s += "https://github.com/hopglass/hopglass</a>."
|
||||||
|
|
||||||
el.innerHTML = s
|
el.innerHTML = s
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,18 @@ define([], function () {
|
||||||
var n = Object.create(data)
|
var n = Object.create(data)
|
||||||
n.nodes = {}
|
n.nodes = {}
|
||||||
|
|
||||||
for (var key in data.nodes) {
|
for (var key in data.nodes)
|
||||||
n.nodes[key] = data.nodes[key].filter(filter)
|
n.nodes[key] = data.nodes[key].filter(filter)
|
||||||
}
|
|
||||||
|
|
||||||
var filteredIds = new Set()
|
var filteredIds = new Set()
|
||||||
|
|
||||||
n.graph = {}
|
n.graph = {}
|
||||||
n.graph.nodes = data.graph.nodes.filter( function (d) {
|
n.graph.nodes = data.graph.nodes.filter( function (d) {
|
||||||
if (!d.node)
|
var r
|
||||||
return true
|
if (d.node)
|
||||||
|
r = filter(d.node)
|
||||||
var r = filter(d.node)
|
else
|
||||||
|
r = filter({})
|
||||||
|
|
||||||
if (r)
|
if (r)
|
||||||
filteredIds.add(d.id)
|
filteredIds.add(d.id)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
define(["d3"], function (d3) {
|
define(["d3"], function (d3) {
|
||||||
var margin = 200
|
var margin = 200
|
||||||
var NODE_RADIUS = 15
|
var NODE_RADIUS = 15
|
||||||
var LINE_RADIUS = 12
|
var LINE_RADIUS = 7
|
||||||
|
|
||||||
return function (config, linkScale, sidebar, router) {
|
return function (config, linkScale, sidebar, router) {
|
||||||
var self = this
|
var self = this
|
||||||
|
@ -19,6 +19,7 @@ define(["d3"], function (d3) {
|
||||||
var nodes = []
|
var nodes = []
|
||||||
var uplinkNodes = []
|
var uplinkNodes = []
|
||||||
var nonUplinkNodes = []
|
var nonUplinkNodes = []
|
||||||
|
var unseenNodes = []
|
||||||
var unknownNodes = []
|
var unknownNodes = []
|
||||||
var savedPanZoom
|
var savedPanZoom
|
||||||
|
|
||||||
|
@ -213,7 +214,7 @@ define(["d3"], function (d3) {
|
||||||
|
|
||||||
function drawLabel(d) {
|
function drawLabel(d) {
|
||||||
var neighbours = d.neighbours.filter(function (d) {
|
var neighbours = d.neighbours.filter(function (d) {
|
||||||
return d.link.o.type !== "VPN"
|
return !d.link.o.isVPN
|
||||||
})
|
})
|
||||||
|
|
||||||
var sum = neighbours.reduce(function (a, b) {
|
var sum = neighbours.reduce(function (a, b) {
|
||||||
|
@ -241,7 +242,8 @@ define(["d3"], function (d3) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function visibleLinks(d) {
|
function visibleLinks(d) {
|
||||||
return (d.source.x > screenRect.left && d.source.x < screenRect.right &&
|
return (d.o.isVPN ||
|
||||||
|
d.source.x > screenRect.left && d.source.x < screenRect.right &&
|
||||||
d.source.y > screenRect.top && d.source.y < screenRect.bottom) ||
|
d.source.y > screenRect.top && d.source.y < screenRect.bottom) ||
|
||||||
(d.target.x > screenRect.left && d.target.x < screenRect.right &&
|
(d.target.x > screenRect.left && d.target.x < screenRect.right &&
|
||||||
d.target.y > screenRect.top && d.target.y < screenRect.bottom)
|
d.target.y > screenRect.top && d.target.y < screenRect.bottom)
|
||||||
|
@ -313,6 +315,7 @@ define(["d3"], function (d3) {
|
||||||
var unknownColor = "#D10E2A"
|
var unknownColor = "#D10E2A"
|
||||||
var nonUplinkColor = "#F2E3C6"
|
var nonUplinkColor = "#F2E3C6"
|
||||||
var uplinkColor = "#5BAAEB"
|
var uplinkColor = "#5BAAEB"
|
||||||
|
var unseenColor = "#FFA726"
|
||||||
var highlightColor = "rgba(252, 227, 198, 0.15)"
|
var highlightColor = "rgba(252, 227, 198, 0.15)"
|
||||||
var nodeRadius = 6
|
var nodeRadius = 6
|
||||||
var cableColor = "#50B0F0"
|
var cableColor = "#50B0F0"
|
||||||
|
@ -322,16 +325,19 @@ define(["d3"], function (d3) {
|
||||||
links.forEach(function (d) {
|
links.forEach(function (d) {
|
||||||
var dx = d.target.x - d.source.x
|
var dx = d.target.x - d.source.x
|
||||||
var dy = d.target.y - d.source.y
|
var dy = d.target.y - d.source.y
|
||||||
var a = Math.sqrt(dx * dx + dy * dy)
|
var a = Math.sqrt(dx * dx + dy * dy) * 2
|
||||||
dx /= a
|
dx /= a
|
||||||
dy /= a
|
dy /= a
|
||||||
|
|
||||||
|
var distancex = d.target.x - d.source.x - (10 * dx)
|
||||||
|
var distancey = d.target.y - d.source.y - (10 * dy)
|
||||||
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.moveTo(d.source.x + dx * nodeRadius, d.source.y + dy * nodeRadius)
|
ctx.moveTo(d.source.x + dx * nodeRadius, d.source.y + dy * nodeRadius)
|
||||||
ctx.lineTo(d.target.x - dx * nodeRadius, d.target.y - dy * nodeRadius)
|
ctx.lineTo(d.target.x - (distancex / 2) - dx * nodeRadius, d.target.y - (distancey / 2) - dy * nodeRadius)
|
||||||
ctx.strokeStyle = d.o.type === "Kabel" ? cableColor : d.color
|
ctx.strokeStyle = d.o.type === "Kabel" ? cableColor : d.color
|
||||||
ctx.globalAlpha = d.o.type === "VPN" ? 0.1 : 0.8
|
ctx.globalAlpha = d.o.isVPN ? 0.1 : 0.8
|
||||||
ctx.lineWidth = d.o.type === "VPN" ? 1.5 : 2.5
|
ctx.lineWidth = d.o.isVPN ? 1.5 : 2.5
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -364,11 +370,17 @@ define(["d3"], function (d3) {
|
||||||
ctx.drawImage(uplinkNode, scale * r * d.x - uplinkNode.width / 2, scale * r * d.y - uplinkNode.height / 2)
|
ctx.drawImage(uplinkNode, scale * r * d.x - uplinkNode.width / 2, scale * r * d.y - uplinkNode.height / 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var unseenNode = drawNode(unseenColor, nodeRadius, scale, r)
|
||||||
|
unseenNodes.filter(visibleNodes).forEach(function (d) {
|
||||||
|
ctx.drawImage(unseenNode, scale * r * d.x - unseenNode.width / 2, scale * r * d.y - unseenNode.height / 2)
|
||||||
|
})
|
||||||
|
|
||||||
ctx.restore()
|
ctx.restore()
|
||||||
|
|
||||||
// -- draw clients --
|
// -- draw clients --
|
||||||
ctx.save()
|
ctx.save()
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
|
if (scale > 0.9)
|
||||||
nodes.filter(visibleNodes).forEach(function (d) {
|
nodes.filter(visibleNodes).forEach(function (d) {
|
||||||
var clients = d.o.node.statistics.clients
|
var clients = d.o.node.statistics.clients
|
||||||
if (clients === 0)
|
if (clients === 0)
|
||||||
|
@ -463,32 +475,34 @@ define(["d3"], function (d3) {
|
||||||
requestAnimationFrame(redraw)
|
requestAnimationFrame(redraw)
|
||||||
}
|
}
|
||||||
|
|
||||||
function distance(a, b) {
|
function distance(ax, ay, bx, by) {
|
||||||
return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)
|
return Math.pow(ax - bx, 2) + Math.pow(ay - by, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
function distancePoint(a, b) {
|
function distancePoint(a, b) {
|
||||||
return Math.sqrt(distance(a, b))
|
return Math.sqrt(distance(a.x, a.y, b.x, b.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
function distanceLink(p, a, b) {
|
function distanceLink(p, a, b) {
|
||||||
/* http://stackoverflow.com/questions/849211 */
|
/* http://stackoverflow.com/questions/849211 */
|
||||||
|
|
||||||
var l2 = distance(a, b)
|
var bx = b.x - ((b.x - a.x) / 2)
|
||||||
|
var by = b.y - ((b.y - a.y) / 2)
|
||||||
|
|
||||||
|
var l2 = distance(a.x, a.y, bx, by)
|
||||||
|
|
||||||
if (l2 === 0)
|
if (l2 === 0)
|
||||||
return distance(p, a)
|
return distance(p.x, p.y, a.x, a.y)
|
||||||
|
|
||||||
var t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / l2
|
var t = ((p.x - a.x) * (bx - a.x) + (p.y - a.y) * (by - a.y)) / l2
|
||||||
|
|
||||||
if (t < 0)
|
if (t < 0)
|
||||||
return distance(p, a)
|
return distance(p.x, p.y, a.x, a.y)
|
||||||
|
|
||||||
if (t > 1)
|
if (t > 1)
|
||||||
return distance(p, b)
|
return distance(p.x, p.y, bx, by)
|
||||||
|
|
||||||
return Math.sqrt(distance(p, { x: a.x + t * (b.x - a.x),
|
return Math.sqrt(distance(p.x, p.y, a.x + t * (bx - a.x), a.y + t * (by - a.y) ))
|
||||||
y: a.y + t * (b.y - a.y) }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateXY(d) {
|
function translateXY(d) {
|
||||||
|
@ -516,7 +530,7 @@ define(["d3"], function (d3) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var links = intLinks.filter(function (d) {
|
var links = intLinks.filter(function (d) {
|
||||||
return d.o.type !== "VPN"
|
return !d.o.isVPN
|
||||||
}).filter(function (d) {
|
}).filter(function (d) {
|
||||||
return distanceLink(e, d.source, d.target) < LINE_RADIUS
|
return distanceLink(e, d.source, d.target) < LINE_RADIUS
|
||||||
})
|
})
|
||||||
|
@ -577,13 +591,13 @@ define(["d3"], function (d3) {
|
||||||
.charge(-250)
|
.charge(-250)
|
||||||
.gravity(0.1)
|
.gravity(0.1)
|
||||||
.linkDistance(function (d) {
|
.linkDistance(function (d) {
|
||||||
if (d.o.type === "VPN")
|
if (d.o.isVPN)
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
return LINK_DISTANCE
|
return LINK_DISTANCE
|
||||||
})
|
})
|
||||||
.linkStrength(function (d) {
|
.linkStrength(function (d) {
|
||||||
if (d.o.type === "VPN")
|
if (d.o.isVPN)
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
return Math.max(0.5, 1 / d.o.tq)
|
return Math.max(0.5, 1 / d.o.tq)
|
||||||
|
@ -637,7 +651,7 @@ define(["d3"], function (d3) {
|
||||||
e.source = newNodesDict[d.source.id]
|
e.source = newNodesDict[d.source.id]
|
||||||
e.target = newNodesDict[d.target.id]
|
e.target = newNodesDict[d.target.id]
|
||||||
|
|
||||||
if (d.type === "VPN")
|
if (d.isVPN)
|
||||||
e.color = "rgba(255, 255, 255, " + (0.6 / d.tq) + ")"
|
e.color = "rgba(255, 255, 255, " + (0.6 / d.tq) + ")"
|
||||||
else
|
else
|
||||||
e.color = linkScale(d.tq).hex()
|
e.color = linkScale(d.tq).hex()
|
||||||
|
@ -685,19 +699,29 @@ define(["d3"], function (d3) {
|
||||||
d.source.neighbours[d.target.o.id] = {node: d.target, link: d}
|
d.source.neighbours[d.target.o.id] = {node: d.target, link: d}
|
||||||
d.target.neighbours[d.source.o.id] = {node: d.source, link: d}
|
d.target.neighbours[d.source.o.id] = {node: d.source, link: d}
|
||||||
|
|
||||||
if (d.o.source.node && d.o.target.node)
|
if (d.o.source && d.o.target)
|
||||||
linksDict[d.o.id] = d
|
linksDict[d.o.id] = d
|
||||||
})
|
})
|
||||||
|
|
||||||
|
intLinks.forEach(function (d) {
|
||||||
|
if (linksDict[d.target.o.node_id + "-" + d.source.o.node_id])
|
||||||
|
return
|
||||||
|
|
||||||
|
var obj = { source: d.target, target: d.source, o: { isVPN: d.o.isVPN, type: "dead", id: d.target.o.node_id + "-" + d.source.o.node_id, tq: 1 }, color: "rgba(255, 255, 255, 0.6)" }
|
||||||
|
intLinks.push(obj)
|
||||||
|
linksDict[d.target.o.node_id + "-" + d.source.o.node_id] = obj
|
||||||
|
})
|
||||||
|
|
||||||
intNodes.forEach(function (d) {
|
intNodes.forEach(function (d) {
|
||||||
d.neighbours = Object.keys(d.neighbours).map(function (k) {
|
d.neighbours = Object.keys(d.neighbours).map(function (k) {
|
||||||
return d.neighbours[k]
|
return d.neighbours[k]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
nodes = intNodes.filter(function (d) { return d.o.node })
|
nodes = intNodes.filter(function (d) { return !d.o.unseen && d.o.node })
|
||||||
uplinkNodes = nodes.filter(function (d) { return d.o.node.flags.uplink })
|
uplinkNodes = nodes.filter(function (d) { return d.o.node.flags.uplink })
|
||||||
nonUplinkNodes = nodes.filter(function (d) { return !d.o.node.flags.uplink })
|
nonUplinkNodes = nodes.filter(function (d) { return !d.o.node.flags.uplink })
|
||||||
|
unseenNodes = intNodes.filter(function (d) { return d.o.unseen && d.o.node })
|
||||||
unknownNodes = intNodes.filter(function (d) { return !d.o.node })
|
unknownNodes = intNodes.filter(function (d) { return !d.o.node })
|
||||||
|
|
||||||
if (localStorageTest()) {
|
if (localStorageTest()) {
|
||||||
|
|
|
@ -48,6 +48,9 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var loader = document.getElementsByClassName("loader")[0]
|
||||||
|
loader.classList.add("hide")
|
||||||
|
|
||||||
contentDiv = document.createElement("div")
|
contentDiv = document.createElement("div")
|
||||||
contentDiv.classList.add("content")
|
contentDiv.classList.add("content")
|
||||||
document.body.appendChild(contentDiv)
|
document.body.appendChild(contentDiv)
|
||||||
|
@ -57,7 +60,7 @@ function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist,
|
||||||
contentDiv.appendChild(buttons)
|
contentDiv.appendChild(buttons)
|
||||||
|
|
||||||
var buttonToggle = document.createElement("button")
|
var buttonToggle = document.createElement("button")
|
||||||
buttonToggle.textContent = ""
|
buttonToggle.textContent = "\uF133"
|
||||||
buttonToggle.onclick = function () {
|
buttonToggle.onclick = function () {
|
||||||
if (content.constructor === Map)
|
if (content.constructor === Map)
|
||||||
router.view("g")
|
router.view("g")
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
define(function () {
|
define(function () {
|
||||||
|
function showStatImg(o, d) {
|
||||||
|
var subst = {}
|
||||||
|
subst["{SOURCE}"] = d.source.node_id
|
||||||
|
subst["{SOURCE_NAME}"] = d.source.node.nodeinfo.hostname ? d.source.node.nodeinfo.hostname : "unknown"
|
||||||
|
subst["{TARGET}"] = d.target.node_id
|
||||||
|
subst["{TARGET_NAME}"] = d.target.node.nodeinfo.hostname ? d.target.node.nodeinfo.hostname : "unknown"
|
||||||
|
return showStat(o, subst)
|
||||||
|
}
|
||||||
|
|
||||||
return function (config, el, router, d) {
|
return function (config, el, router, d) {
|
||||||
|
var unknown = !(d.source.node)
|
||||||
var h2 = document.createElement("h2")
|
var h2 = document.createElement("h2")
|
||||||
var a1 = document.createElement("a")
|
var a1 = document.createElement("a")
|
||||||
|
if (!unknown) {
|
||||||
a1.href = "#"
|
a1.href = "#"
|
||||||
a1.onclick = router.node(d.source.node)
|
a1.onclick = router.node(d.source.node)
|
||||||
a1.textContent = d.source.node.nodeinfo.hostname
|
}
|
||||||
|
a1.textContent = unknown ? d.source.id : d.source.node.nodeinfo.hostname
|
||||||
h2.appendChild(a1)
|
h2.appendChild(a1)
|
||||||
h2.appendChild(document.createTextNode(" → "))
|
h2.appendChild(document.createTextNode(" → "))
|
||||||
var a2 = document.createElement("a")
|
var a2 = document.createElement("a")
|
||||||
|
@ -20,10 +32,17 @@ define(function () {
|
||||||
attributeEntry(attributes, "TQ", showTq(d))
|
attributeEntry(attributes, "TQ", showTq(d))
|
||||||
attributeEntry(attributes, "Entfernung", showDistance(d))
|
attributeEntry(attributes, "Entfernung", showDistance(d))
|
||||||
attributeEntry(attributes, "Typ", d.type)
|
attributeEntry(attributes, "Typ", d.type)
|
||||||
var hw1 = dictGet(d.source.node.nodeinfo, ["hardware", "model"])
|
var hw1 = unknown ? null : dictGet(d.source.node.nodeinfo, ["hardware", "model"])
|
||||||
var hw2 = dictGet(d.target.node.nodeinfo, ["hardware", "model"])
|
var hw2 = dictGet(d.target.node.nodeinfo, ["hardware", "model"])
|
||||||
attributeEntry(attributes, "Hardware", (hw1 != null ? hw1 : "unbekannt") + " – " + (hw2 != null ? hw2 : "unbekannt"))
|
attributeEntry(attributes, "Hardware", (hw1 != null ? hw1 : "unbekannt") + " – " + (hw2 != null ? hw2 : "unbekannt"))
|
||||||
|
|
||||||
el.appendChild(attributes)
|
el.appendChild(attributes)
|
||||||
|
|
||||||
|
if (config.linkInfos)
|
||||||
|
config.linkInfos.forEach( function (linkInfo) {
|
||||||
|
var h4 = document.createElement("h4")
|
||||||
|
h4.textContent = linkInfo.name
|
||||||
|
el.appendChild(h4)
|
||||||
|
el.appendChild(showStatImg(linkInfo, d))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,92 +1,100 @@
|
||||||
define(function () {
|
define(function () {
|
||||||
return function (config, el, router, d) {
|
return function (config, el, router, d) {
|
||||||
var h2 = document.createElement("h2")
|
var sidebarTitle = document.createElement("h2")
|
||||||
h2.textContent = "Location: " + d.toString()
|
sidebarTitle.textContent = "Location: " + d.toString()
|
||||||
el.appendChild(h2)
|
el.appendChild(sidebarTitle)
|
||||||
|
|
||||||
getJSON("https://nominatim.openstreetmap.org/reverse?format=json&lat=" + d.lat + "&lon=" + d.lng + "&zoom=18&addressdetail=0")
|
getJSON("https://nominatim.openstreetmap.org/reverse?format=json&lat=" + d.lat + "&lon=" + d.lng + "&zoom=18&addressdetails=0")
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
h2.textContent = result.display_name
|
if(result.display_name)
|
||||||
|
sidebarTitle.textContent = result.display_name
|
||||||
})
|
})
|
||||||
|
|
||||||
var h3lat = document.createElement("h3")
|
var editLat = document.createElement("input")
|
||||||
h3lat.textContent = "Breitengrad"
|
editLat.type = "text"
|
||||||
el.appendChild(h3lat)
|
editLat.value = d.lat.toFixed(9)
|
||||||
var txt1 = document.createElement("textarea")
|
el.appendChild(createBox("lat", "Breitengrad", editLat))
|
||||||
txt1.id = "location-latitude"
|
|
||||||
txt1.value = d.lat.toFixed(9)
|
|
||||||
var p = document.createElement("p")
|
|
||||||
p.appendChild(txt1)
|
|
||||||
p.appendChild(createCopyButton(txt1.id))
|
|
||||||
el.appendChild(p)
|
|
||||||
|
|
||||||
var h3lng = document.createElement("h3")
|
var editLng = document.createElement("input")
|
||||||
h3lng.textContent = "Längengrad"
|
editLng.type = "text"
|
||||||
el.appendChild(h3lng)
|
editLng.value = d.lng.toFixed(9)
|
||||||
var txt2 = document.createElement("textarea")
|
el.appendChild(createBox("lng", "Längengrad", editLng))
|
||||||
txt2.id = "location-longitude"
|
|
||||||
txt2.value = d.lng.toFixed(9)
|
|
||||||
var p2 = document.createElement("p")
|
|
||||||
p2.appendChild(txt2)
|
|
||||||
p2.appendChild(createCopyButton(txt2.id))
|
|
||||||
el.appendChild(p2)
|
|
||||||
|
|
||||||
var a1 = document.createElement("a")
|
var editUci = document.createElement("textarea")
|
||||||
a1.textContent = "plain"
|
editUci.value =
|
||||||
a1.onclick = function() {
|
"uci set gluon-node-info.@location[0]='location'; " +
|
||||||
|
"uci set gluon-node-info.@location[0].share_location='1';" +
|
||||||
|
"uci set gluon-node-info.@location[0].latitude='" + d.lat.toFixed(9) + "';" +
|
||||||
|
"uci set gluon-node-info.@location[0].longitude='" + d.lng.toFixed(9) + "';" +
|
||||||
|
"uci commit gluon-node-info"
|
||||||
|
|
||||||
|
el.appendChild(createBox("uci", "Befehl", editUci, false))
|
||||||
|
|
||||||
|
var linkPlain = document.createElement("a")
|
||||||
|
linkPlain.textContent = "plain"
|
||||||
|
linkPlain.onclick = function() {
|
||||||
switch2plain()
|
switch2plain()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
a1.href = config.siteURL
|
linkPlain.href = "#"
|
||||||
var a2 = document.createElement("a")
|
|
||||||
a2.textContent = "uci"
|
var linkUci = document.createElement("a")
|
||||||
a2.onclick = function() {
|
linkUci.textContent = "uci"
|
||||||
|
linkUci.onclick = function() {
|
||||||
switch2uci()
|
switch2uci()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
a2.href = config.siteURL
|
linkUci.href = "#"
|
||||||
|
|
||||||
var p3 = document.createElement("p")
|
var hintText = document.createElement("p")
|
||||||
p3.textContent = "Du kannst zwischen "
|
hintText.appendChild(document.createTextNode("Du kannst zwischen "))
|
||||||
p3.appendChild(a1)
|
hintText.appendChild(linkPlain)
|
||||||
var t1 = document.createTextNode(" und ")
|
hintText.appendChild(document.createTextNode(" und "))
|
||||||
p3.appendChild(t1)
|
hintText.appendChild(linkUci)
|
||||||
p3.appendChild(a2)
|
hintText.appendChild(document.createTextNode(" wechseln."))
|
||||||
var t2 = document.createTextNode(" wechseln.")
|
el.appendChild(hintText)
|
||||||
p3.appendChild(t2)
|
|
||||||
el.appendChild(p3)
|
|
||||||
|
|
||||||
function createCopyButton(id) {
|
function createBox(name, title, inputElem, isVisible) {
|
||||||
|
var visible = typeof isVisible !== "undefined" ? isVisible : true
|
||||||
|
var box = document.createElement("div")
|
||||||
|
var heading = document.createElement("h3")
|
||||||
|
heading.textContent = title
|
||||||
|
box.appendChild(heading)
|
||||||
var btn = document.createElement("button")
|
var btn = document.createElement("button")
|
||||||
btn.className = "ion-ios-copy"
|
btn.className = "ion-ios-copy"
|
||||||
btn.title = "Kopiere"
|
btn.title = "Kopieren"
|
||||||
btn.onclick = function() {
|
btn.onclick = function() { copy2clip(inputElem.id) }
|
||||||
copy2clip(id)
|
inputElem.id = "location-" + name
|
||||||
}
|
inputElem.readOnly = true
|
||||||
return btn
|
var line = document.createElement("p")
|
||||||
|
line.appendChild(inputElem)
|
||||||
|
line.appendChild(btn)
|
||||||
|
box.appendChild(line)
|
||||||
|
box.id = "box-" + name
|
||||||
|
box.style.display = visible ? "block" : "none"
|
||||||
|
return box
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy2clip(id) {
|
function copy2clip(id) {
|
||||||
var copyTextarea = document.querySelector("#" + id)
|
var copyField = document.querySelector("#" + id)
|
||||||
copyTextarea.select()
|
copyField.select()
|
||||||
try {
|
try {
|
||||||
var successful = document.execCommand("copy")
|
document.execCommand("copy")
|
||||||
var msg = successful ? "successful" : "unsuccessful"
|
|
||||||
console.log("Copying text command was " + msg)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Oops, unable to copy")
|
console.log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function switch2plain() {
|
function switch2plain() {
|
||||||
var box1 = document.getElementById("location-latitude")
|
document.getElementById("box-uci").style.display = "none"
|
||||||
box1.value = d.lat.toFixed(9)
|
document.getElementById("box-lat").style.display = "block"
|
||||||
var box2 = document.getElementById("location-longitude")
|
document.getElementById("box-lng").style.display = "block"
|
||||||
box2.value = d.lng.toFixed(9)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function switch2uci() {
|
function switch2uci() {
|
||||||
var box1 = document.getElementById("location-latitude")
|
document.getElementById("box-uci").style.display = "block"
|
||||||
box1.value = "uci set gluon-node-info.@location[0].latitude='" + d.lat.toFixed(9) + "'"
|
document.getElementById("box-lat").style.display = "none"
|
||||||
var box2 = document.getElementById("location-longitude")
|
document.getElementById("box-lng").style.display = "none"
|
||||||
box2.value = "uci set gluon-node-info.@location[0].longitude='" + d.lng.toFixed(9) + "'"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -42,7 +42,6 @@ define(["infobox/link", "infobox/node", "infobox/location"], function (Link, Nod
|
||||||
}
|
}
|
||||||
|
|
||||||
self.gotoLocation = function (d) {
|
self.gotoLocation = function (d) {
|
||||||
console.log("goto location called with ", d)
|
|
||||||
create()
|
create()
|
||||||
new Location(config, el, router, d)
|
new Location(config, el, router, d)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,11 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
|
|
||||||
function showStatus(d) {
|
function showStatus(d) {
|
||||||
return function (el) {
|
return function (el) {
|
||||||
el.classList.add(d.flags.online ? "online" : "offline")
|
el.classList.add(d.flags.unseen ? "unseen" : (d.flags.online ? "online" : "offline"))
|
||||||
el.textContent = (d.flags.online ? "online " : "offline, " + d.lastseen.fromNow(true)) + " (Stand " + d.lastseen.format("DD.MM.YYYY, H:mm:ss)")
|
if (d.flags.online)
|
||||||
|
el.textContent = "online, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"
|
||||||
|
else
|
||||||
|
el.textContent = "offline, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY, H:mm:ss") + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,18 +81,124 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
return d.firstseen.fromNow(true)
|
return d.firstseen.fromNow(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wifiChannelAlias(ch) {
|
||||||
|
var chlist = {
|
||||||
|
"1": "2412 MHz",
|
||||||
|
"2": "2417 MHz",
|
||||||
|
"3": "2422 MHz",
|
||||||
|
"4": "2427 MHz",
|
||||||
|
"5": "2432 MHz",
|
||||||
|
"6": "2437 MHz",
|
||||||
|
"7": "2442 MHz",
|
||||||
|
"8": "2447 MHz",
|
||||||
|
"9": "2452 MHz",
|
||||||
|
"10": "2457 MHz",
|
||||||
|
"11": "2462 MHz",
|
||||||
|
"12": "2467 MHz",
|
||||||
|
"13": "2472 MHz",
|
||||||
|
"36": "5180 MHz (Indoors)",
|
||||||
|
"40": "5200 MHz (Indoors)",
|
||||||
|
"44": "5220 MHz (Indoors)",
|
||||||
|
"48": "5240 MHz (Indoors)",
|
||||||
|
"52": "5260 MHz (Indoors/DFS/TPC)",
|
||||||
|
"56": "5280 MHz (Indoors/DFS/TPC)",
|
||||||
|
"60": "5300 MHz (Indoors/DFS/TPC)",
|
||||||
|
"64": "5320 MHz (Indoors/DFS/TPC)",
|
||||||
|
"100": "5500 MHz (DFS) !!",
|
||||||
|
"104": "5520 MHz (DFS) !!",
|
||||||
|
"108": "5540 MHz (DFS) !!",
|
||||||
|
"112": "5560 MHz (DFS) !!",
|
||||||
|
"116": "5580 MHz (DFS) !!",
|
||||||
|
"120": "5600 MHz (DFS) !!",
|
||||||
|
"124": "5620 MHz (DFS) !!",
|
||||||
|
"128": "5640 MHz (DFS) !!",
|
||||||
|
"132": "5660 MHz (DFS) !!",
|
||||||
|
"136": "5680 MHz (DFS) !!",
|
||||||
|
"140": "5700 MHz (DFS) !!"
|
||||||
|
}
|
||||||
|
if (!(ch in chlist))
|
||||||
|
return ""
|
||||||
|
else
|
||||||
|
return chlist[ch]
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWifiChannel(ch) {
|
||||||
|
if (!ch)
|
||||||
|
return undefined
|
||||||
|
|
||||||
|
return ch + " (" + wifiChannelAlias(ch) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
function showClients(d) {
|
function showClients(d) {
|
||||||
if (!d.flags.online)
|
if (!d.flags.online)
|
||||||
return undefined
|
return undefined
|
||||||
|
|
||||||
|
var meshclients = getMeshClients(d)
|
||||||
|
resetMeshClients(d)
|
||||||
|
var before = " ("
|
||||||
|
var after = " in der lokalen Wolke)"
|
||||||
return function (el) {
|
return function (el) {
|
||||||
el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine"))
|
el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : "keine"))
|
||||||
|
el.appendChild(document.createTextNode(before))
|
||||||
|
el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine"))
|
||||||
|
el.appendChild(document.createTextNode(after))
|
||||||
el.appendChild(document.createElement("br"))
|
el.appendChild(document.createElement("br"))
|
||||||
|
|
||||||
var span = document.createElement("span")
|
var span = document.createElement("span")
|
||||||
span.classList.add("clients")
|
span.classList.add("clients")
|
||||||
span.textContent = " ".repeat(d.statistics.clients)
|
span.textContent = " ".repeat(d.statistics.clients)
|
||||||
el.appendChild(span)
|
el.appendChild(span)
|
||||||
|
|
||||||
|
var spanmesh = document.createElement("span")
|
||||||
|
spanmesh.classList.add("clientsMesh")
|
||||||
|
spanmesh.textContent = " ".repeat(meshclients - d.statistics.clients)
|
||||||
|
el.appendChild(spanmesh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMeshClients(node) {
|
||||||
|
var meshclients = 0
|
||||||
|
if (node.statistics && !isNaN(node.statistics.clients))
|
||||||
|
meshclients = node.statistics.clients
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if (node.parsed)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
node.parsed = 1
|
||||||
|
node.neighbours.forEach(function (neighbour) {
|
||||||
|
if (!neighbour.link.isVPN && neighbour.node)
|
||||||
|
meshclients += getMeshClients(neighbour.node)
|
||||||
|
})
|
||||||
|
|
||||||
|
return meshclients
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetMeshClients(node) {
|
||||||
|
if (!node.parsed)
|
||||||
|
return
|
||||||
|
|
||||||
|
node.parsed = 0
|
||||||
|
|
||||||
|
node.neighbours.forEach(function (neighbour) {
|
||||||
|
if (!neighbour.link.isVPN && neighbour.node)
|
||||||
|
resetMeshClients(neighbour.node)
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMeshClients(d) {
|
||||||
|
if (!d.flags.online)
|
||||||
|
return undefined
|
||||||
|
|
||||||
|
var meshclients = getMeshClients(d)
|
||||||
|
resetMeshClients(d)
|
||||||
|
return function (el) {
|
||||||
|
el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine"))
|
||||||
|
el.appendChild(document.createElement("br"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +218,9 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
var a = document.createElement("a")
|
var a = document.createElement("a")
|
||||||
|
if (ip.includes("."))
|
||||||
|
a.href = "http://" + ip + "/"
|
||||||
|
else
|
||||||
a.href = "http://[" + ip + "]/"
|
a.href = "http://[" + ip + "]/"
|
||||||
a.textContent = ip
|
a.textContent = ip
|
||||||
el.appendChild(a)
|
el.appendChild(a)
|
||||||
|
@ -153,7 +265,7 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
}
|
}
|
||||||
|
|
||||||
var label = document.createElement("label")
|
var label = document.createElement("label")
|
||||||
label.textContent = (v)
|
label.textContent = +(Math.round(v + "e+2") + "e-2")
|
||||||
span.appendChild(label)
|
span.appendChild(label)
|
||||||
|
|
||||||
return span
|
return span
|
||||||
|
@ -177,6 +289,65 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showAirtime(band, val) {
|
||||||
|
if (!val)
|
||||||
|
return undefined
|
||||||
|
|
||||||
|
return function (el) {
|
||||||
|
el.appendChild(showBar("airtime" + band.toString(), val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLink(target, router) {
|
||||||
|
if (!target) return document.createTextNode("unknown")
|
||||||
|
var unknown = !(target.node)
|
||||||
|
var text = unknown ? (target.id ? target.id : target) : target.node.nodeinfo.hostname
|
||||||
|
if (!unknown) {
|
||||||
|
var link = document.createElement("a")
|
||||||
|
link.classList.add("hostname-link")
|
||||||
|
link.href = "#"
|
||||||
|
link.onclick = router.node(target.node)
|
||||||
|
link.textContent = text
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
return document.createTextNode(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
function showGateway(d, router) {
|
||||||
|
var nh
|
||||||
|
if (dictGet(d.statistics, ["nexthop"]))
|
||||||
|
nh = dictGet(d.statistics, ["nexthop"])
|
||||||
|
if (dictGet(d.statistics, ["gateway_nexthop"]))
|
||||||
|
nh = dictGet(d.statistics, ["gateway_nexthop"])
|
||||||
|
var gw = dictGet(d.statistics, ["gateway"])
|
||||||
|
|
||||||
|
if (!gw) return null
|
||||||
|
return function (el) {
|
||||||
|
var num = 0
|
||||||
|
while (gw && nh && gw.id !== nh.id && num < 10) {
|
||||||
|
if (num !== 0) el.appendChild(document.createTextNode(" -> "))
|
||||||
|
el.appendChild(createLink(nh, router))
|
||||||
|
num++
|
||||||
|
if (!nh.node || !nh.node.statistics) break
|
||||||
|
if (!dictGet(nh.node.statistics, ["gateway"]) || !dictGet(nh.node.statistics, ["gateway"]).id) break
|
||||||
|
if (dictGet(nh.node.statistics, ["gateway"]).id !== gw.id) break
|
||||||
|
if (dictGet(nh.node.statistics, ["gateway_nexthop"]))
|
||||||
|
nh = dictGet(nh.node.statistics, ["gateway_nexthop"])
|
||||||
|
else if (dictGet(nh.node.statistics, ["nexthop"]))
|
||||||
|
nh = dictGet(nh.node.statistics, ["nexthop"])
|
||||||
|
else
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (gw && nh && gw.id !== nh.id) {
|
||||||
|
if (num !== 0) el.appendChild(document.createTextNode(" -> "))
|
||||||
|
num++
|
||||||
|
el.appendChild(document.createTextNode("..."))
|
||||||
|
}
|
||||||
|
if (num !== 0) el.appendChild(document.createTextNode(" -> "))
|
||||||
|
el.appendChild(createLink(gw, router))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showPages(d) {
|
function showPages(d) {
|
||||||
var webpages = dictGet(d.nodeinfo, ["pages"])
|
var webpages = dictGet(d.nodeinfo, ["pages"])
|
||||||
if (webpages === null)
|
if (webpages === null)
|
||||||
|
@ -216,46 +387,68 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
return au.enabled ? "aktiviert (" + au.branch + ")" : "deaktiviert"
|
return au.enabled ? "aktiviert (" + au.branch + ")" : "deaktiviert"
|
||||||
}
|
}
|
||||||
|
|
||||||
function showStatImg(o, nodeId) {
|
function showNodeImg(o, model) {
|
||||||
var content, caption
|
if (!model)
|
||||||
|
return document.createTextNode("Knotenname")
|
||||||
|
|
||||||
|
var content, caption
|
||||||
|
var modelhash = model.split("").reduce(function(a, b) {
|
||||||
|
a = ((a << 5) - a) + b.charCodeAt(0)
|
||||||
|
return a & a
|
||||||
|
}, 0)
|
||||||
|
|
||||||
if (o.thumbnail) {
|
|
||||||
content = document.createElement("img")
|
content = document.createElement("img")
|
||||||
content.src = o.thumbnail.replace("{NODE_ID}", nodeId)
|
content.id = "routerpicture"
|
||||||
|
content.classList.add("nodeImg")
|
||||||
|
content.src = o.thumbnail.replace("{MODELHASH}", modelhash)
|
||||||
|
content.onerror = function() {
|
||||||
|
document.getElementById("routerpicdiv").outerHTML = "Knotenname"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o.caption) {
|
if (o.caption) {
|
||||||
caption = o.caption.replace("{NODE_ID}", nodeId)
|
caption = o.caption.replace("{MODELHASH}", modelhash)
|
||||||
|
|
||||||
if (!content)
|
if (!content)
|
||||||
content = document.createTextNode(caption)
|
content = document.createTextNode(caption)
|
||||||
}
|
}
|
||||||
|
|
||||||
var p = document.createElement("p")
|
var p = document.createElement("p")
|
||||||
|
|
||||||
if (o.href) {
|
|
||||||
var link = document.createElement("a")
|
|
||||||
link.target = "_blank"
|
|
||||||
link.href = o.href.replace("{NODE_ID}", nodeId)
|
|
||||||
link.appendChild(content)
|
|
||||||
|
|
||||||
if (caption && o.thumbnail)
|
|
||||||
link.title = caption
|
|
||||||
|
|
||||||
p.appendChild(link)
|
|
||||||
} else
|
|
||||||
p.appendChild(content)
|
p.appendChild(content)
|
||||||
|
|
||||||
return p
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
function showStatImg(o, d) {
|
||||||
|
var subst = {}
|
||||||
|
subst["{NODE_ID}"] = d.nodeinfo.node_id ? d.nodeinfo.node_id : "unknown"
|
||||||
|
subst["{NODE_NAME}"] = d.nodeinfo.hostname ? d.nodeinfo.hostname : "unknown"
|
||||||
|
return showStat(o, subst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(config, el, router, d) {
|
return function(config, el, router, d) {
|
||||||
|
var attributes = document.createElement("table")
|
||||||
|
attributes.classList.add("attributes")
|
||||||
|
|
||||||
|
if (config.hwImg) {
|
||||||
|
var top = document.createElement("div")
|
||||||
|
top.id = "routerpicdiv"
|
||||||
|
try {
|
||||||
|
config.hwImg.forEach(function(hwImg) {
|
||||||
|
try {
|
||||||
|
top.appendChild(showNodeImg(hwImg, dictGet(d, ["nodeinfo", "hardware", "model"])))
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err.message)
|
||||||
|
}
|
||||||
|
attributeEntry(attributes, top, d.nodeinfo.hostname)
|
||||||
|
} else {
|
||||||
var h2 = document.createElement("h2")
|
var h2 = document.createElement("h2")
|
||||||
h2.textContent = d.nodeinfo.hostname
|
h2.textContent = d.nodeinfo.hostname
|
||||||
el.appendChild(h2)
|
el.appendChild(h2)
|
||||||
|
}
|
||||||
var attributes = document.createElement("table")
|
|
||||||
attributes.classList.add("attributes")
|
|
||||||
|
|
||||||
attributeEntry(attributes, "Status", showStatus(d))
|
attributeEntry(attributes, "Status", showStatus(d))
|
||||||
attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null)
|
attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null)
|
||||||
|
@ -271,13 +464,17 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
attributeEntry(attributes, "Site", showSite(d, config))
|
attributeEntry(attributes, "Site", showSite(d, config))
|
||||||
attributeEntry(attributes, "Uptime", showUptime(d))
|
attributeEntry(attributes, "Uptime", showUptime(d))
|
||||||
attributeEntry(attributes, "Teil des Netzes", showFirstseen(d))
|
attributeEntry(attributes, "Teil des Netzes", showFirstseen(d))
|
||||||
|
attributeEntry(attributes, "Kanal 2.4 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan2"])))
|
||||||
|
attributeEntry(attributes, "Kanal 5 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan5"])))
|
||||||
|
attributeEntry(attributes, "Airtime 2.4 GHz", showAirtime(2, dictGet(d.statistics, ["wireless", "airtime2"])))
|
||||||
|
attributeEntry(attributes, "Airtime 5 GHz", showAirtime(5, dictGet(d.statistics, ["wireless", "airtime5"])))
|
||||||
attributeEntry(attributes, "Systemlast", showLoad(d))
|
attributeEntry(attributes, "Systemlast", showLoad(d))
|
||||||
attributeEntry(attributes, "Arbeitsspeicher", showRAM(d))
|
attributeEntry(attributes, "Arbeitsspeicher", showRAM(d))
|
||||||
attributeEntry(attributes, "IP Adressen", showIPs(d))
|
attributeEntry(attributes, "IP Adressen", showIPs(d))
|
||||||
attributeEntry(attributes, "Webseite", showPages(d))
|
attributeEntry(attributes, "Webseite", showPages(d))
|
||||||
attributeEntry(attributes, "Gewähltes Gateway", dictGet(d.statistics, ["gateway"]))
|
attributeEntry(attributes, "Gewähltes Gateway", showGateway(d, router))
|
||||||
attributeEntry(attributes, "Autom. Updates", showAutoupdate(d))
|
attributeEntry(attributes, "Autom. Updates", showAutoupdate(d))
|
||||||
attributeEntry(attributes, "Clients", showClients(d))
|
attributeEntry(attributes, "Clients", showClients(d), showMeshClients(d))
|
||||||
|
|
||||||
el.appendChild(attributes)
|
el.appendChild(attributes)
|
||||||
|
|
||||||
|
@ -287,7 +484,7 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
var h4 = document.createElement("h4")
|
var h4 = document.createElement("h4")
|
||||||
h4.textContent = nodeInfo.name
|
h4.textContent = nodeInfo.name
|
||||||
el.appendChild(h4)
|
el.appendChild(h4)
|
||||||
el.appendChild(showStatImg(nodeInfo, d.nodeinfo.node_id))
|
el.appendChild(showStatImg(nodeInfo, d))
|
||||||
})
|
})
|
||||||
|
|
||||||
if (d.neighbours.length > 0) {
|
if (d.neighbours.length > 0) {
|
||||||
|
@ -326,6 +523,7 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
var tbody = document.createElement("tbody")
|
var tbody = document.createElement("tbody")
|
||||||
|
|
||||||
d.neighbours.forEach( function (d) {
|
d.neighbours.forEach( function (d) {
|
||||||
|
var unknown = !(d.node)
|
||||||
var tr = document.createElement("tr")
|
var tr = document.createElement("tr")
|
||||||
|
|
||||||
var td1 = document.createElement("td")
|
var td1 = document.createElement("td")
|
||||||
|
@ -333,14 +531,9 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
|
||||||
tr.appendChild(td1)
|
tr.appendChild(td1)
|
||||||
|
|
||||||
var td2 = document.createElement("td")
|
var td2 = document.createElement("td")
|
||||||
var a1 = document.createElement("a")
|
td2.appendChild(createLink(d, router))
|
||||||
a1.classList.add("hostname")
|
|
||||||
a1.textContent = d.node.nodeinfo.hostname
|
|
||||||
a1.href = "#"
|
|
||||||
a1.onclick = router.node(d.node)
|
|
||||||
td2.appendChild(a1)
|
|
||||||
|
|
||||||
if (has_location(d.node)) {
|
if (!unknown && has_location(d.node)) {
|
||||||
var span = document.createElement("span")
|
var span = document.createElement("span")
|
||||||
span.classList.add("icon")
|
span.classList.add("icon")
|
||||||
span.classList.add("ion-location")
|
span.classList.add("ion-location")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
define(["sorttable", "virtual-dom"], function (SortTable, V) {
|
define(["sorttable", "virtual-dom"], function (SortTable, V) {
|
||||||
function linkName(d) {
|
function linkName(d) {
|
||||||
return d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname
|
return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + d.target.node.nodeinfo.hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
var headings = [{ name: "Knoten",
|
var headings = [{ name: "Knoten",
|
||||||
|
|
94
lib/main.js
94
lib/main.js
|
@ -4,12 +4,14 @@ function (moment, Router, L, GUI, numeral) {
|
||||||
function handleData(data) {
|
function handleData(data) {
|
||||||
var dataNodes = {}
|
var dataNodes = {}
|
||||||
dataNodes.nodes = []
|
dataNodes.nodes = []
|
||||||
|
dataNodes.nodeIds = []
|
||||||
var dataGraph = {}
|
var dataGraph = {}
|
||||||
dataGraph.batadv = {}
|
dataGraph.batadv = {}
|
||||||
dataGraph.batadv.nodes = []
|
dataGraph.batadv.nodes = []
|
||||||
dataGraph.batadv.links = []
|
dataGraph.batadv.links = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function rearrangeLinks(d) {
|
function rearrangeLinks(d) {
|
||||||
d.source += dataGraph.batadv.nodes.length
|
d.source += dataGraph.batadv.nodes.length
|
||||||
d.target += dataGraph.batadv.nodes.length
|
d.target += dataGraph.batadv.nodes.length
|
||||||
|
@ -32,11 +34,22 @@ function (moment, Router, L, GUI, numeral) {
|
||||||
vererr = "Unsupported nodes version: " + data[i].version
|
vererr = "Unsupported nodes version: " + data[i].version
|
||||||
console.log(vererr) //silent fail
|
console.log(vererr) //silent fail
|
||||||
} else {
|
} else {
|
||||||
dataNodes.nodes = dataNodes.nodes.concat(data[i].nodes)
|
data[i].nodes.forEach(fillData)
|
||||||
dataNodes.timestamp = data[i].timestamp
|
dataNodes.timestamp = data[i].timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fillData (node) {
|
||||||
|
var position = dataNodes.nodeIds.indexOf(node.nodeinfo.node_id)
|
||||||
|
if(position === -1){
|
||||||
|
dataNodes.nodes.push(node)
|
||||||
|
dataNodes.nodeIds.push(node.nodeinfo.node_id)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if(node.flags.online === true)
|
||||||
|
dataNodes.nodes[position] = node
|
||||||
|
}
|
||||||
|
|
||||||
var nodes = dataNodes.nodes.filter( function (d) {
|
var nodes = dataNodes.nodes.filter( function (d) {
|
||||||
return "firstseen" in d && "lastseen" in d
|
return "firstseen" in d && "lastseen" in d
|
||||||
})
|
})
|
||||||
|
@ -61,15 +74,17 @@ function (moment, Router, L, GUI, numeral) {
|
||||||
var graph = dataGraph.batadv
|
var graph = dataGraph.batadv
|
||||||
|
|
||||||
graph.nodes.forEach( function (d) {
|
graph.nodes.forEach( function (d) {
|
||||||
if (d.node_id in graphnodes)
|
if (d.node_id in graphnodes) {
|
||||||
d.node = graphnodes[d.node_id]
|
d.node = graphnodes[d.node_id]
|
||||||
|
if (d.unseen) {
|
||||||
|
d.node.flags.online = true
|
||||||
|
d.node.flags.unseen = true
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
graph.links.forEach( function (d) {
|
graph.links.forEach( function (d) {
|
||||||
if (graph.nodes[d.source].node)
|
|
||||||
d.source = graph.nodes[d.source]
|
d.source = graph.nodes[d.source]
|
||||||
else
|
|
||||||
d.source = undefined
|
|
||||||
|
|
||||||
if (graph.nodes[d.target].node)
|
if (graph.nodes[d.target].node)
|
||||||
d.target = graph.nodes[d.target]
|
d.target = graph.nodes[d.target]
|
||||||
|
@ -78,14 +93,25 @@ function (moment, Router, L, GUI, numeral) {
|
||||||
})
|
})
|
||||||
|
|
||||||
var links = graph.links.filter( function (d) {
|
var links = graph.links.filter( function (d) {
|
||||||
return d.source !== undefined && d.target !== undefined
|
return d.target !== undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
links.forEach( function (d) {
|
links.forEach( function (d) {
|
||||||
var ids = [d.source.node.nodeinfo.node_id, d.target.node.nodeinfo.node_id]
|
var unknown = (d.source.node === undefined)
|
||||||
d.id = ids.sort().join("-")
|
var ids
|
||||||
|
if (unknown)
|
||||||
|
ids = [d.source.id.replace(/:/g, ""), d.target.node.nodeinfo.node_id]
|
||||||
|
else
|
||||||
|
ids = [d.source.node.nodeinfo.node_id, d.target.node.nodeinfo.node_id]
|
||||||
|
d.id = ids.join("-")
|
||||||
|
|
||||||
if (!("location" in d.source.node.nodeinfo && "location" in d.target.node.nodeinfo))
|
if (unknown ||
|
||||||
|
!d.source.node.nodeinfo.location ||
|
||||||
|
!d.target.node.nodeinfo.location ||
|
||||||
|
isNaN(d.source.node.nodeinfo.location.latitude) ||
|
||||||
|
isNaN(d.source.node.nodeinfo.location.longitude) ||
|
||||||
|
isNaN(d.target.node.nodeinfo.location.latitude) ||
|
||||||
|
isNaN(d.target.node.nodeinfo.location.longitude))
|
||||||
return
|
return
|
||||||
|
|
||||||
d.latlngs = []
|
d.latlngs = []
|
||||||
|
@ -97,21 +123,53 @@ function (moment, Router, L, GUI, numeral) {
|
||||||
|
|
||||||
nodes.forEach( function (d) {
|
nodes.forEach( function (d) {
|
||||||
d.neighbours = []
|
d.neighbours = []
|
||||||
|
if (d.statistics) {
|
||||||
|
/*eslint camelcase:0*/
|
||||||
|
if ("gateway" in d.statistics && d.statistics.gateway in graphnodes)
|
||||||
|
d.statistics.gateway = {"node": graphnodes[d.statistics.gateway], "id": d.statistics.gateway}
|
||||||
|
if ("nexthop" in d.statistics && d.statistics.nexthop in graphnodes)
|
||||||
|
d.statistics.nexthop = {"node": graphnodes[d.statistics.nexthop], "id": d.statistics.nexthop}
|
||||||
|
if ("gateway_nexthop" in d.statistics && d.statistics.gateway_nexthop in graphnodes)
|
||||||
|
d.statistics.gateway_nexthop = {"node": graphnodes[d.statistics.gateway_nexthop], "id": d.statistics.gateway_nexthop}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
links.forEach( function (d) {
|
links.forEach( function (d) {
|
||||||
|
if (d.type === "tunnel" || d.vpn) {
|
||||||
|
d.type = "VPN"
|
||||||
|
d.isVPN = true
|
||||||
|
} else if (d.type === "fastd") {
|
||||||
|
d.type = "fastd"
|
||||||
|
d.isVPN = true
|
||||||
|
} else if (d.type === "l2tp") {
|
||||||
|
d.type = "L2TP"
|
||||||
|
d.isVPN = true
|
||||||
|
} else if (d.type === "gre") {
|
||||||
|
d.type = "GRE"
|
||||||
|
d.isVPN = true
|
||||||
|
} else if (d.type === "wireless") {
|
||||||
|
d.type = "Wifi"
|
||||||
|
d.isVPN = false
|
||||||
|
} else if (d.type === "other") {
|
||||||
|
d.type = "Kabel"
|
||||||
|
d.isVPN = false
|
||||||
|
} else {
|
||||||
|
d.type = "N/A"
|
||||||
|
d.isVPN = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.isVPN && d.target.node)
|
||||||
|
d.target.node.flags.uplink = true
|
||||||
|
|
||||||
|
var unknown = (d.source.node === undefined)
|
||||||
|
if (unknown) {
|
||||||
|
d.target.node.neighbours.push({ id: d.source.id, link: d, incoming: true })
|
||||||
|
return
|
||||||
|
}
|
||||||
d.source.node.neighbours.push({ node: d.target.node, link: d, incoming: false })
|
d.source.node.neighbours.push({ node: d.target.node, link: d, incoming: false })
|
||||||
d.target.node.neighbours.push({ node: d.source.node, link: d, incoming: true })
|
d.target.node.neighbours.push({ node: d.source.node, link: d, incoming: true })
|
||||||
if (d.type !== "tunnel")
|
if (!d.isVPN)
|
||||||
d.source.node.meshlinks = d.source.node.meshlinks ? d.source.node.meshlinks + 1 : 1
|
d.source.node.meshlinks = d.source.node.meshlinks ? d.source.node.meshlinks + 1 : 1
|
||||||
if (d.type === "tunnel")
|
|
||||||
d.type = "VPN"
|
|
||||||
else if (d.type === "wireless")
|
|
||||||
d.type = "Wifi"
|
|
||||||
else if (d.type === "other")
|
|
||||||
d.type = "Kabel"
|
|
||||||
else
|
|
||||||
d.type = "N/A"
|
|
||||||
})
|
})
|
||||||
|
|
||||||
links.sort( function (a, b) {
|
links.sort( function (a, b) {
|
||||||
|
|
39
lib/map.js
39
lib/map.js
|
@ -18,7 +18,7 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
|
|
||||||
onAdd: function () {
|
onAdd: function () {
|
||||||
var button = L.DomUtil.create("button", "add-layer")
|
var button = L.DomUtil.create("button", "add-layer")
|
||||||
button.textContent = ""
|
button.textContent = "\uF2C7"
|
||||||
|
|
||||||
// L.DomEvent.disableClickPropagation(button)
|
// L.DomEvent.disableClickPropagation(button)
|
||||||
// Click propagation isn't disabled as this causes problems with the
|
// Click propagation isn't disabled as this causes problems with the
|
||||||
|
@ -46,7 +46,7 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
|
|
||||||
onAdd: function () {
|
onAdd: function () {
|
||||||
var button = L.DomUtil.create("button", "locate-user")
|
var button = L.DomUtil.create("button", "locate-user")
|
||||||
button.textContent = ""
|
button.textContent = "\uF2E9"
|
||||||
|
|
||||||
L.DomEvent.disableClickPropagation(button)
|
L.DomEvent.disableClickPropagation(button)
|
||||||
L.DomEvent.addListener(button, "click", this.onClick, this)
|
L.DomEvent.addListener(button, "click", this.onClick, this)
|
||||||
|
@ -85,7 +85,7 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
|
|
||||||
onAdd: function () {
|
onAdd: function () {
|
||||||
var button = L.DomUtil.create("button", "coord-picker")
|
var button = L.DomUtil.create("button", "coord-picker")
|
||||||
button.textContent = ""
|
button.textContent = "\uF2A6"
|
||||||
|
|
||||||
// Click propagation isn't disabled as this causes problems with the
|
// Click propagation isn't disabled as this causes problems with the
|
||||||
// location picking mode; instead propagation is stopped in onClick().
|
// location picking mode; instead propagation is stopped in onClick().
|
||||||
|
@ -172,7 +172,7 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
|
|
||||||
var map, userLocation
|
var map, userLocation
|
||||||
var layerControl
|
var layerControl
|
||||||
var customLayers = new Set()
|
var customLayers = {}
|
||||||
var baseLayers = {}
|
var baseLayers = {}
|
||||||
|
|
||||||
var locateUserButton = new LocateButton(function (d) {
|
var locateUserButton = new LocateButton(function (d) {
|
||||||
|
@ -259,23 +259,22 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
if (layerName in baseLayers)
|
if (layerName in baseLayers)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (customLayers.has(layerName))
|
if (layerName in customLayers)
|
||||||
return
|
return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var layer = L.tileLayer.provider(layerName)
|
var layer = L.tileLayer.provider(layerName)
|
||||||
layerControl.addBaseLayer(layer, layerName)
|
layerControl.addBaseLayer(layer, layerName)
|
||||||
customLayers.add(layerName)
|
customLayers[layerName] = layer
|
||||||
|
|
||||||
if (localStorageTest())
|
if (localStorageTest())
|
||||||
localStorage.setItem("map/customLayers", JSON.stringify(Array.from(customLayers)))
|
localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers)))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function contextMenuGotoLocation(e) {
|
function contextMenuGotoLocation(e) {
|
||||||
console.log("context menu called at ", e)
|
|
||||||
router.gotoLocation(e.latlng)
|
router.gotoLocation(e.latlng)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +286,7 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
var layers = config.mapLayers.map( function (d) {
|
var layers = config.mapLayers.map( function (d) {
|
||||||
return {
|
return {
|
||||||
"name": d.name,
|
"name": d.name,
|
||||||
"layer": "url" in d ? L.tileLayer(d.url, d.config) : L.tileLayer.provider(d.name)
|
"layer": "url" in d ? "layers" in d.config ? L.tileLayer.wms(d.url, d.config) : L.tileLayer(d.url, d.config) : L.tileLayer.provider(d.name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -319,16 +318,33 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
|
|
||||||
if (d)
|
if (d)
|
||||||
d.forEach(addLayer)
|
d.forEach(addLayer)
|
||||||
|
|
||||||
|
d = JSON.parse(localStorage.getItem("map/selectedLayer"))
|
||||||
|
d = d && d.name in baseLayers ? baseLayers[d.name] : d && d.name in customLayers ? customLayers[d.name] : false
|
||||||
|
|
||||||
|
if (d) {
|
||||||
|
map.removeLayer(layers[0].layer)
|
||||||
|
map.addLayer(d)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientLayer = new ClientLayer({minZoom: 15})
|
var clientLayer = new ClientLayer({minZoom: 15})
|
||||||
clientLayer.addTo(map)
|
clientLayer.addTo(map)
|
||||||
clientLayer.setZIndex(5)
|
clientLayer.setZIndex(5)
|
||||||
|
|
||||||
var labelsLayer = new LabelsLayer()
|
var labelsLayer = new LabelsLayer({})
|
||||||
labelsLayer.addTo(map)
|
labelsLayer.addTo(map)
|
||||||
labelsLayer.setZIndex(6)
|
labelsLayer.setZIndex(6)
|
||||||
|
|
||||||
|
map.on("baselayerchange", function(e) {
|
||||||
|
map.options.maxZoom = e.layer.options.maxZoom
|
||||||
|
clientLayer.options.maxZoom = map.options.maxZoom
|
||||||
|
labelsLayer.options.maxZoom = map.options.maxZoom
|
||||||
|
if (map.getZoom() > map.options.maxZoom) map.setZoom(map.options.maxZoom)
|
||||||
|
if (localStorageTest())
|
||||||
|
localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name}))
|
||||||
|
})
|
||||||
|
|
||||||
var nodeDict = {}
|
var nodeDict = {}
|
||||||
var linkDict = {}
|
var linkDict = {}
|
||||||
var highlight
|
var highlight
|
||||||
|
@ -448,7 +464,10 @@ define(["map/clientlayer", "map/labelslayer",
|
||||||
var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router)
|
var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router)
|
||||||
groupLines = L.featureGroup(lines).addTo(map)
|
groupLines = L.featureGroup(lines).addTo(map)
|
||||||
|
|
||||||
|
if (typeof config.fixedCenter === "undefined")
|
||||||
barycenter = calcBarycenter(data.nodes.all.filter(has_location))
|
barycenter = calcBarycenter(data.nodes.all.filter(has_location))
|
||||||
|
else
|
||||||
|
barycenter = L.circle(L.latLng(new L.LatLng(config.fixedCenter.lat, config.fixedCenter.lng)), config.fixedCenter.radius * 1000)
|
||||||
|
|
||||||
var nodesOnline = subtract(data.nodes.all.filter(online), data.nodes.new)
|
var nodesOnline = subtract(data.nodes.all.filter(online), data.nodes.new)
|
||||||
var nodesOffline = subtract(data.nodes.all.filter(offline), data.nodes.lost)
|
var nodesOffline = subtract(data.nodes.all.filter(offline), data.nodes.lost)
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
define(["leaflet", "jshashes"],
|
define(["leaflet"],
|
||||||
function (L, jsHashes) {
|
function (L) {
|
||||||
var MD5 = new jsHashes.MD5()
|
|
||||||
|
|
||||||
return L.TileLayer.Canvas.extend({
|
return L.TileLayer.Canvas.extend({
|
||||||
setData: function (d) {
|
setData: function (d) {
|
||||||
this.data = d
|
this.data = d
|
||||||
|
|
||||||
//pre-calculate start angles
|
//pre-calculate start angles
|
||||||
this.data.all().forEach(function (d) {
|
this.data.all().forEach(function (d) {
|
||||||
var hash = MD5.hex(d.node.nodeinfo.node_id)
|
d.startAngle = (parseInt(d.node.nodeinfo.node_id.substr(10, 2), 16) / 255) * 2 * Math.PI
|
||||||
d.startAngle = (parseInt(hash.substr(0, 2), 16) / 255) * 2 * Math.PI
|
|
||||||
})
|
})
|
||||||
this.redraw()
|
this.redraw()
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,16 +6,22 @@ define(function () {
|
||||||
self.setData = function (d) {
|
self.setData = function (d) {
|
||||||
var totalNodes = sum(d.nodes.all.map(one))
|
var totalNodes = sum(d.nodes.all.map(one))
|
||||||
var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one))
|
var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one))
|
||||||
|
var totalOfflineNodes = sum(d.nodes.all.filter(function (node) {return !node.flags.online}).map(one))
|
||||||
var totalNewNodes = sum(d.nodes.new.map(one))
|
var totalNewNodes = sum(d.nodes.new.map(one))
|
||||||
var totalLostNodes = sum(d.nodes.lost.map(one))
|
var totalLostNodes = sum(d.nodes.lost.map(one))
|
||||||
var totalClients = sum(d.nodes.all.filter(online).map( function (d) {
|
var totalClients = sum(d.nodes.all.filter(online).map( function (d) {
|
||||||
return d.statistics.clients
|
return d.statistics.clients ? d.statistics.clients : 0
|
||||||
}))
|
}))
|
||||||
var totalGateways = sum(d.nodes.all.filter(online).filter( function (d) {
|
var totalGateways = sum(Array.from(new Set(d.nodes.all.filter(online).map( function(d) {
|
||||||
|
return ("gateway" in d.statistics && d.statistics.gateway.id) ? d.statistics.gateway.id : d.statistics.gateway
|
||||||
|
}).concat(d.nodes.all.filter( function (d) {
|
||||||
return d.flags.gateway
|
return d.flags.gateway
|
||||||
}).map(one))
|
})))).map(function(d) {
|
||||||
|
return (typeof d === "string") ? 1 : 0
|
||||||
|
}))
|
||||||
|
|
||||||
var nodetext = [{ count: totalOnlineNodes, label: "online" },
|
var nodetext = [{ count: totalOnlineNodes, label: "online" },
|
||||||
|
{ count: totalOfflineNodes, label: "offline" },
|
||||||
{ count: totalNewNodes, label: "neu" },
|
{ count: totalNewNodes, label: "neu" },
|
||||||
{ count: totalLostNodes, label: "verschwunden" }
|
{ count: totalLostNodes, label: "verschwunden" }
|
||||||
].filter( function (d) { return d.count > 0 } )
|
].filter( function (d) { return d.count > 0 } )
|
||||||
|
|
|
@ -21,7 +21,11 @@ define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral)
|
||||||
|
|
||||||
var headings = [{ name: "Knoten",
|
var headings = [{ name: "Knoten",
|
||||||
sort: function (a, b) {
|
sort: function (a, b) {
|
||||||
return a.nodeinfo.hostname.localeCompare(b.nodeinfo.hostname)
|
var aname = typeof a.nodeinfo.hostname === "string" ? a.nodeinfo.hostname : a.nodeinfo.node_id
|
||||||
|
var bname = typeof b.nodeinfo.hostname === "string" ? b.nodeinfo.hostname : b.nodeinfo.node_id
|
||||||
|
if (typeof aname === "string" && typeof bname === "string")
|
||||||
|
return aname.localeCompare(bname)
|
||||||
|
return typeof aname === "string" ? 1 : typeof bname === "string" ? -1 : 0
|
||||||
},
|
},
|
||||||
reverse: false
|
reverse: false
|
||||||
},
|
},
|
||||||
|
@ -52,7 +56,7 @@ define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral)
|
||||||
|
|
||||||
td1Content.push(V.h("a", { className: aClass.join(" "),
|
td1Content.push(V.h("a", { className: aClass.join(" "),
|
||||||
onclick: router.node(d),
|
onclick: router.node(d),
|
||||||
href: "#"
|
href: "#!n:" + d.nodeinfo.node_id
|
||||||
}, d.nodeinfo.hostname))
|
}, d.nodeinfo.hostname))
|
||||||
|
|
||||||
if (has_location(d))
|
if (has_location(d))
|
||||||
|
|
|
@ -23,43 +23,17 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
|
||||||
var uplinkTable = document.createElement("table")
|
var uplinkTable = document.createElement("table")
|
||||||
uplinkTable.classList.add("proportion")
|
uplinkTable.classList.add("proportion")
|
||||||
|
|
||||||
var gwTable = document.createElement("table")
|
var gwNodesTable = document.createElement("table")
|
||||||
gwTable.classList.add("proportion")
|
gwNodesTable.classList.add("proportion")
|
||||||
|
|
||||||
|
var gwClientsTable = document.createElement("table")
|
||||||
|
gwClientsTable.classList.add("proportion")
|
||||||
|
|
||||||
var siteTable = document.createElement("table")
|
var siteTable = document.createElement("table")
|
||||||
siteTable.classList.add("proportion")
|
siteTable.classList.add("proportion")
|
||||||
|
|
||||||
function showStatGlobal(o) {
|
function showStatGlobal(o) {
|
||||||
var content, caption
|
return showStat(o)
|
||||||
|
|
||||||
if (o.thumbnail) {
|
|
||||||
content = document.createElement("img")
|
|
||||||
content.src = o.thumbnail
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o.caption) {
|
|
||||||
caption = o.caption
|
|
||||||
|
|
||||||
if (!content)
|
|
||||||
content = document.createTextNode(caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
var p = document.createElement("p")
|
|
||||||
|
|
||||||
if (o.href) {
|
|
||||||
var link = document.createElement("a")
|
|
||||||
link.target = "_blank"
|
|
||||||
link.href = o.href
|
|
||||||
link.appendChild(content)
|
|
||||||
|
|
||||||
if (caption && o.thumbnail)
|
|
||||||
link.title = caption
|
|
||||||
|
|
||||||
p.appendChild(link)
|
|
||||||
} else
|
|
||||||
p.appendChild(content)
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(nodes, key, f) {
|
function count(nodes, key, f) {
|
||||||
|
@ -80,6 +54,25 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
|
||||||
return Object.keys(dict).map(function (d) { return [d, dict[d], key, f] })
|
return Object.keys(dict).map(function (d) { return [d, dict[d], key, f] })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function countClients(nodes, key, f) {
|
||||||
|
var dict = {}
|
||||||
|
|
||||||
|
nodes.forEach( function (d) {
|
||||||
|
var v = dictGet(d, key.slice(0))
|
||||||
|
|
||||||
|
if (f !== undefined)
|
||||||
|
v = f(v)
|
||||||
|
|
||||||
|
if (v === null)
|
||||||
|
return
|
||||||
|
|
||||||
|
dict[v] = d.statistics.clients + (v in dict ? dict[v] : 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
return Object.keys(dict).map(function (d) { return [d, dict[d], key, f] })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function addFilter(filter) {
|
function addFilter(filter) {
|
||||||
return function () {
|
return function () {
|
||||||
filterManager.addFilter(filter)
|
filterManager.addFilter(filter)
|
||||||
|
@ -135,10 +128,17 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
|
||||||
return d ? "online" : "offline"
|
return d ? "online" : "offline"
|
||||||
})
|
})
|
||||||
var fwDict = count(nodes, ["nodeinfo", "software", "firmware", "release"])
|
var fwDict = count(nodes, ["nodeinfo", "software", "firmware", "release"])
|
||||||
var hwDict = count(nodes, ["nodeinfo", "hardware", "model"])
|
var hwDict = count(nodes, ["nodeinfo", "hardware", "model"], function (d) {
|
||||||
var geoDict = count(nodes, ["nodeinfo", "location"], function (d) {
|
if (d) {
|
||||||
return d ? "ja" : "nein"
|
d = d.replace(/\(r\)|\(tm\)/gi, "").replace(/AMD |Intel |TP-Link | CPU| Processor/g, "")
|
||||||
|
if (d.indexOf("@") > 0) d = d.substring(0, d.indexOf("@"))
|
||||||
|
}
|
||||||
|
return d
|
||||||
})
|
})
|
||||||
|
var geoDict = count(nodes, ["nodeinfo", "location"], function (d) {
|
||||||
|
return d && d.longitude && d.latitude ? "ja" : "nein"
|
||||||
|
})
|
||||||
|
|
||||||
var autoDict = count(nodes, ["nodeinfo", "software", "autoupdater"], function (d) {
|
var autoDict = count(nodes, ["nodeinfo", "software", "autoupdater"], function (d) {
|
||||||
if (d === null)
|
if (d === null)
|
||||||
return null
|
return null
|
||||||
|
@ -147,21 +147,38 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
|
||||||
else
|
else
|
||||||
return "(deaktiviert)"
|
return "(deaktiviert)"
|
||||||
})
|
})
|
||||||
|
|
||||||
var uplinkDict = count(nodes, ["flags", "uplink"], function (d) {
|
var uplinkDict = count(nodes, ["flags", "uplink"], function (d) {
|
||||||
return d ? "ja" : "nein"
|
return d ? "ja" : "nein"
|
||||||
})
|
})
|
||||||
|
|
||||||
var gwDict = count(nodes, ["statistics", "gateway"], function (d) {
|
var gwNodesDict = count(onlineNodes, ["statistics", "gateway"], function (d) {
|
||||||
if (d === null)
|
if (d === null)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
if (d in nodeDict)
|
if (d.node)
|
||||||
return nodeDict[d].nodeinfo.hostname
|
return d.node.nodeinfo.hostname
|
||||||
|
|
||||||
|
if (d.id)
|
||||||
|
return d.id
|
||||||
|
|
||||||
return d
|
return d
|
||||||
})
|
})
|
||||||
|
|
||||||
var siteDict = count(onlineNodes, ["nodeinfo", "system", "site_code"], function (d) {
|
var gwClientsDict = countClients(onlineNodes, ["statistics", "gateway"], function (d) {
|
||||||
|
if (d === null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (d.node)
|
||||||
|
return d.node.nodeinfo.hostname
|
||||||
|
|
||||||
|
if (d.id)
|
||||||
|
return d.id
|
||||||
|
|
||||||
|
return d
|
||||||
|
})
|
||||||
|
|
||||||
|
var siteDict = count(nodes, ["nodeinfo", "system", "site_code"], function (d) {
|
||||||
var rt = d
|
var rt = d
|
||||||
if (config.siteNames)
|
if (config.siteNames)
|
||||||
config.siteNames.forEach( function (t) {
|
config.siteNames.forEach( function (t) {
|
||||||
|
@ -175,71 +192,45 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
|
||||||
fillTable("Firmware", fwTable, fwDict.sort(function (a, b) { return vercomp(b[0], a[0]) }))
|
fillTable("Firmware", fwTable, fwDict.sort(function (a, b) { return vercomp(b[0], a[0]) }))
|
||||||
fillTable("Hardware", hwTable, hwDict.sort(function (a, b) { return b[1] - a[1] }))
|
fillTable("Hardware", hwTable, hwDict.sort(function (a, b) { return b[1] - a[1] }))
|
||||||
fillTable("Koordinaten", geoTable, geoDict.sort(function (a, b) { return b[1] - a[1] }))
|
fillTable("Koordinaten", geoTable, geoDict.sort(function (a, b) { return b[1] - a[1] }))
|
||||||
fillTable("Autom. Updates", autoTable, autoDict.sort(function (a, b) { return b[1] - a[1] }))
|
|
||||||
fillTable("Uplink", uplinkTable, uplinkDict.sort(function (a, b) { return b[1] - a[1] }))
|
fillTable("Uplink", uplinkTable, uplinkDict.sort(function (a, b) { return b[1] - a[1] }))
|
||||||
fillTable("Gewähltes Gateway", gwTable, gwDict.sort(function (a, b) { return b[1] - a[1] }))
|
fillTable("Autom. Updates", autoTable, autoDict.sort(function (a, b) { return b[1] - a[1] }))
|
||||||
|
fillTable("Gateway", gwNodesTable, gwNodesDict.sort(function (a, b) { return b[1] - a[1] }))
|
||||||
|
fillTable("Gateway", gwClientsTable, gwClientsDict.sort(function (a, b) { return b[1] - a[1] }))
|
||||||
fillTable("Site", siteTable, siteDict.sort(function (a, b) { return b[1] - a[1] }))
|
fillTable("Site", siteTable, siteDict.sort(function (a, b) { return b[1] - a[1] }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
self.render = function (el) {
|
self.render = function (el) {
|
||||||
var h2
|
var h2
|
||||||
h2 = document.createElement("h2")
|
self.renderSingle(el, "Status", statusTable)
|
||||||
h2.textContent = "Status"
|
self.renderSingle(el, "Nodes an Gateway", gwNodesTable)
|
||||||
el.appendChild(h2)
|
self.renderSingle(el, "Clients an Gateway", gwClientsTable)
|
||||||
el.appendChild(statusTable)
|
self.renderSingle(el, "Firmwareversionen", fwTable)
|
||||||
|
self.renderSingle(el, "Uplink", uplinkTable)
|
||||||
h2 = document.createElement("h2")
|
self.renderSingle(el, "Hardwaremodelle", hwTable)
|
||||||
h2.textContent = "Firmwareversionen"
|
self.renderSingle(el, "Auf der Karte sichtbar", geoTable)
|
||||||
el.appendChild(h2)
|
self.renderSingle(el, "Autoupdater", autoTable)
|
||||||
el.appendChild(fwTable)
|
self.renderSingle(el, "Site", siteTable)
|
||||||
|
|
||||||
if(config.siteNames || config.showSites) {
|
|
||||||
h2 = document.createElement("h2")
|
|
||||||
h2.textContent = "Orte"
|
|
||||||
el.appendChild(h2)
|
|
||||||
el.appendChild(siteTable)
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 = document.createElement("h2")
|
|
||||||
h2.textContent = "Hardwaremodelle"
|
|
||||||
el.appendChild(h2)
|
|
||||||
el.appendChild(hwTable)
|
|
||||||
|
|
||||||
h2 = document.createElement("h2")
|
|
||||||
h2.textContent = "Auf der Karte sichtbar"
|
|
||||||
el.appendChild(h2)
|
|
||||||
el.appendChild(geoTable)
|
|
||||||
|
|
||||||
h2 = document.createElement("h2")
|
|
||||||
h2.textContent = "Autoupdater"
|
|
||||||
el.appendChild(h2)
|
|
||||||
el.appendChild(autoTable)
|
|
||||||
|
|
||||||
h2 = document.createElement("h2")
|
|
||||||
h2.textContent = "Uplink"
|
|
||||||
el.appendChild(h2)
|
|
||||||
el.appendChild(uplinkTable)
|
|
||||||
|
|
||||||
h2 = document.createElement("h2")
|
|
||||||
h2.textContent = "Gewählter Gateway"
|
|
||||||
el.appendChild(h2)
|
|
||||||
el.appendChild(gwTable)
|
|
||||||
|
|
||||||
h2 = document.createElement("h2")
|
|
||||||
h2.textContent = "Site"
|
|
||||||
el.appendChild(h2)
|
|
||||||
el.appendChild(siteTable)
|
|
||||||
|
|
||||||
if (config.globalInfos)
|
if (config.globalInfos)
|
||||||
config.globalInfos.forEach( function (globalInfo) {
|
config.globalInfos.forEach(function (globalInfo) {
|
||||||
h2 = document.createElement("h2")
|
h2 = document.createElement("h2")
|
||||||
h2.textContent = globalInfo.name
|
h2.textContent = globalInfo.name
|
||||||
el.appendChild(h2)
|
el.appendChild(h2)
|
||||||
|
|
||||||
el.appendChild(showStatGlobal(globalInfo))
|
el.appendChild(showStatGlobal(globalInfo))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.renderSingle = function (el, heading, table) {
|
||||||
|
var h2
|
||||||
|
h2 = document.createElement("h2")
|
||||||
|
h2.textContent = heading
|
||||||
|
h2.onclick = function () {
|
||||||
|
table.classList.toggle("hidden")
|
||||||
|
}
|
||||||
|
el.appendChild(h2)
|
||||||
|
el.appendChild(table)
|
||||||
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -78,6 +78,8 @@ define(function () {
|
||||||
if (!s)
|
if (!s)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
s = decodeURIComponent(s)
|
||||||
|
|
||||||
if (!s.startsWith("#!"))
|
if (!s.startsWith("#!"))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
@ -94,7 +96,7 @@ define(function () {
|
||||||
var id
|
var id
|
||||||
|
|
||||||
if (args[0] === "n") {
|
if (args[0] === "n") {
|
||||||
id = decodeURIComponent(args[1])
|
id = args[1]
|
||||||
if (id in objects.nodes) {
|
if (id in objects.nodes) {
|
||||||
currentObject = { node: objects.nodes[id] }
|
currentObject = { node: objects.nodes[id] }
|
||||||
gotoNode(objects.nodes[id])
|
gotoNode(objects.nodes[id])
|
||||||
|
@ -103,7 +105,7 @@ define(function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args[0] === "l") {
|
if (args[0] === "l") {
|
||||||
id = decodeURIComponent(args[1])
|
id = args[1]
|
||||||
if (id in objects.links) {
|
if (id in objects.links) {
|
||||||
currentObject = { link: objects.links[id] }
|
currentObject = { link: objects.links[id] }
|
||||||
gotoLink(objects.links[id])
|
gotoLink(objects.links[id])
|
||||||
|
|
|
@ -41,7 +41,7 @@ define(["moment", "virtual-dom"], function (moment, V) {
|
||||||
|
|
||||||
td1Content.push(V.h("a", { className: aClass.join(" "),
|
td1Content.push(V.h("a", { className: aClass.join(" "),
|
||||||
onclick: router.node(d),
|
onclick: router.node(d),
|
||||||
href: "#"
|
href: "#!n:" + d.nodeinfo.node_id
|
||||||
}, d.nodeinfo.hostname))
|
}, d.nodeinfo.hostname))
|
||||||
|
|
||||||
if (has_location(d))
|
if (has_location(d))
|
||||||
|
|
|
@ -20,7 +20,7 @@ define(function () {
|
||||||
|
|
||||||
this.gotoLink = function (d) {
|
this.gotoLink = function (d) {
|
||||||
if (d)
|
if (d)
|
||||||
setTitle(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname)
|
setTitle((d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + d.target.node.nodeinfo.hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.gotoLocation = function() {
|
this.gotoLocation = function() {
|
||||||
|
|
28
package.json
28
package.json
|
@ -1,22 +1,41 @@
|
||||||
{
|
{
|
||||||
"name": "hopglass",
|
"name": "hopglass",
|
||||||
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node -e \"require('grunt').cli()\" '' clean lint"
|
"test": "node -e \"require('grunt').cli()\" '' clean lint"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt": "^0.4.5",
|
"autoprefixer": "^6.3.3",
|
||||||
|
"dart-sass": "^1.16.1",
|
||||||
|
"grunt": "^1.0.3",
|
||||||
"grunt-check-dependencies": "^0.6.0",
|
"grunt-check-dependencies": "^0.6.0",
|
||||||
"grunt-contrib-clean": "^0.6.0",
|
"grunt-contrib-clean": "^0.6.0",
|
||||||
"grunt-contrib-connect": "^0.8.0",
|
"grunt-contrib-connect": "^0.8.0",
|
||||||
"grunt-contrib-copy": "^0.5.0",
|
"grunt-contrib-copy": "^0.5.0",
|
||||||
"grunt-contrib-cssmin": "^0.12.2",
|
"grunt-contrib-cssmin": "^0.12.2",
|
||||||
"grunt-contrib-requirejs": "^0.4.4",
|
"grunt-contrib-requirejs": "^0.4.4",
|
||||||
"grunt-contrib-sass": "^0.9.2",
|
|
||||||
"grunt-contrib-uglify": "^0.5.1",
|
"grunt-contrib-uglify": "^0.5.1",
|
||||||
"grunt-contrib-watch": "^0.6.1",
|
"grunt-contrib-watch": "^0.6.1",
|
||||||
"grunt-eslint": "^10.0.0",
|
"grunt-eslint": "^10.0.0",
|
||||||
"grunt-bower-install-simple": "^1.1.2",
|
"grunt-git-describe": "^2.3.2",
|
||||||
"grunt-git-describe": "^2.3.2"
|
"grunt-postcss": "^0.7.2",
|
||||||
|
"grunt-sass": "^3.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"almond": "^0.3.3",
|
||||||
|
"chroma-js": "^0.7.8",
|
||||||
|
"d3": "^3.5.17",
|
||||||
|
"ionicons": "^2.0.1",
|
||||||
|
"leaflet": "^0.7.7",
|
||||||
|
"leaflet-label": "^0.2.1-0",
|
||||||
|
"leaflet-providers": "^1.5.0",
|
||||||
|
"moment": "^2.23.0",
|
||||||
|
"numeraljs": "^1.5.6",
|
||||||
|
"rbush": "^1.4.3",
|
||||||
|
"requirejs": "^2.3.2",
|
||||||
|
"roboto-fontface": "^0.10.0",
|
||||||
|
"tablesort": "3.0.2",
|
||||||
|
"virtual-dom": "^2.1.1"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"env": {
|
"env": {
|
||||||
|
@ -26,6 +45,7 @@
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
|
"showStat": false,
|
||||||
"attributeEntry": false,
|
"attributeEntry": false,
|
||||||
"dictGet": false,
|
"dictGet": false,
|
||||||
"getJSON": false,
|
"getJSON": false,
|
||||||
|
|
|
@ -28,3 +28,7 @@ h5 {
|
||||||
h6 {
|
h6 {
|
||||||
font-size: 0.67em;
|
font-size: 0.67em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
font-family: Roboto;
|
font-family: Roboto, sans-serif;
|
||||||
font-size: 0.83em;
|
font-size: 0.83em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 0 6pt 6pt !important;
|
padding: 0 6pt 6pt !important;
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
li {
|
li {
|
||||||
border-radius: 20pt;
|
border-radius: 20pt;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0pt 0 0pt 8pt;
|
padding: 0 0 0 8pt;
|
||||||
margin: 3pt;
|
margin: 3pt;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #009ee0;
|
background: #009ee0;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
../bower_components/Leaflet.label/dist/leaflet.label.css
|
../node_modules/leaflet-label/dist/leaflet.label.css
|
|
@ -1 +1 @@
|
||||||
../bower_components/leaflet/dist/leaflet.css
|
../node_modules/leaflet/dist/leaflet.css
|
|
@ -1,5 +1,4 @@
|
||||||
.legend .symbol
|
.legend .symbol {
|
||||||
{
|
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
@ -7,22 +6,18 @@
|
||||||
vertical-align: -5%;
|
vertical-align: -5%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend-new .symbol
|
.legend-new .symbol {
|
||||||
{
|
|
||||||
background-color: #93E929;
|
background-color: #93E929;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend-online .symbol
|
.legend-online .symbol {
|
||||||
{
|
|
||||||
background-color: #1566A9;
|
background-color: #1566A9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend-offline .symbol
|
.legend-offline .symbol {
|
||||||
{
|
|
||||||
background-color: #D43E2A;
|
background-color: #D43E2A;
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend-online, .legend-offline
|
.legend-online, .legend-offline {
|
||||||
{
|
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
23
scss/_loader.scss
Normal file
23
scss/_loader.scss
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
.loader {
|
||||||
|
color: #000000;
|
||||||
|
font-size: 1.8em;
|
||||||
|
line-height: 2;
|
||||||
|
margin: 30vh auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
animation: .6s spinner ease-in-out infinite alternate;
|
||||||
|
border-bottom: 2px solid #000000;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
height: 64px;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spinner {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,13 +35,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes blink {
|
@-webkit-keyframes blink {
|
||||||
0% { opacity: 1.0; }
|
0% {
|
||||||
80% { opacity: 1.0; }
|
opacity: 1.0;
|
||||||
90% { opacity: 0.0; }
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
opacity: 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes blink {
|
@keyframes blink {
|
||||||
0% { opacity: 1.0; }
|
0% {
|
||||||
80% { opacity: 1.0; }
|
opacity: 1.0;
|
||||||
90% { opacity: 0.0; }
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
opacity: 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,25 +22,31 @@ time, mark, audio, video {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HTML5 display-role reset for older browsers */
|
/* HTML5 display-role reset for older browsers */
|
||||||
article, aside, details, figcaption, figure,
|
article, aside, details, figcaption, figure,
|
||||||
footer, header, hgroup, menu, nav, section {
|
footer, header, hgroup, menu, nav, section {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol, ul {
|
ol, ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote, q {
|
blockquote, q {
|
||||||
quotes: none;
|
quotes: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote:before, blockquote:after,
|
blockquote:before, blockquote:after,
|
||||||
q:before, q:after {
|
q:before, q:after {
|
||||||
content: '';
|
content: '';
|
||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
|
|
|
@ -1,19 +1,15 @@
|
||||||
/* Original is in LESS and can be found here: https://gist.github.com/gefangenimnetz/3ef3e18364edf105c5af */
|
/* Original is in LESS and can be found here: https://gist.github.com/gefangenimnetz/3ef3e18364edf105c5af */
|
||||||
|
|
||||||
@mixin shadow($level:1){
|
@mixin shadow($level:1) {
|
||||||
@if $level == 1 {
|
@if $level == 1 {
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
}
|
} @else if $level == 2 {
|
||||||
@else if $level == 2 {
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
|
||||||
box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
|
} @else if $level == 3 {
|
||||||
}
|
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
||||||
@else if $level == 3 {
|
} @else if $level == 4 {
|
||||||
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
|
||||||
}
|
} @else if $level == 5 {
|
||||||
@else if $level == 4 {
|
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.22);
|
||||||
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
|
|
||||||
}
|
|
||||||
@else if $level == 5 {
|
|
||||||
box-shadow: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
.nodeheader {
|
||||||
|
width: 90%;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodeheader img {
|
||||||
|
width: 30%;
|
||||||
|
max-height: 180px;
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 128px;
|
||||||
|
display: block;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodeheader img .none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodeheader span {
|
||||||
|
background-color: silver;
|
||||||
|
background-color: hsla(0, 0%, 100%, 0.5);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: large;
|
||||||
|
z-index: 2;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
@import '_leaflet';
|
@import '_leaflet';
|
||||||
@import '_leaflet.label';
|
@import '_leaflet.label';
|
||||||
@import '_filters';
|
@import '_filters';
|
||||||
|
@import '_loader';
|
||||||
|
|
||||||
$minscreenwidth: 630pt;
|
$minscreenwidth: 630pt;
|
||||||
$sidebarwidth: 420pt;
|
$sidebarwidth: 420pt;
|
||||||
|
@ -44,7 +45,7 @@ $buttondistance: 12pt;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-family: Roboto;
|
font-family: Roboto, sans-serif;
|
||||||
@include shadow(1);
|
@include shadow(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +70,9 @@ $buttondistance: 12pt;
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: 'Roboto Slab', serif;
|
font-family: 'Roboto-Slab', serif;
|
||||||
font-size: 11pt;
|
font-size: 11pt;
|
||||||
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
th.sort-header::selection {
|
th.sort-header::selection {
|
||||||
|
@ -116,6 +118,38 @@ table.attributes td {
|
||||||
line-height: 1.41em;
|
line-height: 1.41em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.attributes tr.routerpic {
|
||||||
|
max-height: 128px;
|
||||||
|
max-width: 128px;
|
||||||
|
min-width: 128px;
|
||||||
|
min-height: 128px;
|
||||||
|
/*height: 128px;*/
|
||||||
|
/*background-color: green;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
table.attributes tr.routerpic td {
|
||||||
|
font-weight: bold;
|
||||||
|
/*background-color: red;*/
|
||||||
|
font-size: large;
|
||||||
|
vertical-align:bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.attributes tr.routerpic th {
|
||||||
|
font-weight: bold;
|
||||||
|
/*background-color: red;*/
|
||||||
|
font-size: large;
|
||||||
|
vertical-align:bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodenamesidebar {
|
||||||
|
position: relative;
|
||||||
|
font-weight: bold;
|
||||||
|
/*background-color: red;*/
|
||||||
|
font-size: large;
|
||||||
|
vertical-align:bottom;
|
||||||
|
bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
.infobox, .container {
|
.infobox, .container {
|
||||||
@include shadow(2);
|
@include shadow(2);
|
||||||
|
@ -127,6 +161,10 @@ table.attributes td {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container table.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
line-height: 1.67em;
|
line-height: 1.67em;
|
||||||
}
|
}
|
||||||
|
@ -139,6 +177,13 @@ table.attributes td {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.infobox .clientsMesh {
|
||||||
|
font-family: "ionicons";
|
||||||
|
color: #dbdbdb;
|
||||||
|
word-spacing: -0.2em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
.infobox {
|
.infobox {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0.25em 0;
|
padding: 0.25em 0;
|
||||||
|
@ -147,6 +192,27 @@ table.attributes td {
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="text"], textarea {
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
|
border-radius: 5px;
|
||||||
|
border: none;
|
||||||
|
line-height: 1.67em;
|
||||||
|
vertical-align: bottom;
|
||||||
|
margin-right: 0.7em;
|
||||||
|
padding: 3px 6px;
|
||||||
|
font-family: monospace;
|
||||||
|
width: 70%;
|
||||||
|
max-width: 500px;
|
||||||
|
min-height: 42px;
|
||||||
|
font-size: 1.15em;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical;
|
||||||
|
overflow: auto;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
@ -155,6 +221,7 @@ button {
|
||||||
@include shadow(1);
|
@include shadow(1);
|
||||||
border-radius: 0.9em;
|
border-radius: 0.9em;
|
||||||
background: rgba(255, 255, 255, 0.7);
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
color: #333;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 1.8em;
|
height: 1.8em;
|
||||||
|
@ -175,7 +242,7 @@ button:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
button:active {
|
button:active {
|
||||||
box-shadow: inset 0px 5px 20px rgba(0, 0, 0, 0.19), inset 0px 3px 6px rgba(0, 0, 0, 0.23);
|
box-shadow: inset 0 5px 20px rgba(0, 0, 0, 0.19), inset 0 3px 6px rgba(0, 0, 0, 0.23);
|
||||||
}
|
}
|
||||||
|
|
||||||
button::-moz-focus-inner {
|
button::-moz-focus-inner {
|
||||||
|
@ -194,6 +261,8 @@ button.close {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
color: rgba(0, 0, 0, 0.5);
|
color: rgba(0, 0, 0, 0.5);
|
||||||
font-family: "ionicons";
|
font-family: "ionicons";
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #dc0067;
|
color: #dc0067;
|
||||||
|
@ -284,6 +353,10 @@ table {
|
||||||
color: #D43E2A !important;
|
color: #D43E2A !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unseen {
|
||||||
|
color: #D89100 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
width: $sidebarwidth;
|
width: $sidebarwidth;
|
||||||
|
@ -291,7 +364,7 @@ table {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $buttondistance;
|
top: $buttondistance;
|
||||||
left: $buttondistance;
|
left: $buttondistance;
|
||||||
margin-bottom: $buttondistance;
|
padding-bottom: $buttondistance;
|
||||||
transition: left 0.5s;
|
transition: left 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,9 +449,9 @@ table {
|
||||||
@media screen and (max-width: 80em) {
|
@media screen and (max-width: 80em) {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
top: 0pt;
|
top: 0;
|
||||||
left: 0pt;
|
left: 0;
|
||||||
margin: 0pt;
|
margin: 0;
|
||||||
width: $sidebarwidthsmall;
|
width: $sidebarwidthsmall;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@include shadow(2);
|
@include shadow(2);
|
||||||
|
@ -409,7 +482,7 @@ table {
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
position: static;
|
position: static;
|
||||||
margin: 0em !important;
|
margin: 0 !important;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
grunt.config.merge({
|
grunt.config.merge({
|
||||||
bowerdir: "bower_components",
|
nodedir: "node_modules",
|
||||||
copy: {
|
copy: {
|
||||||
html: {
|
html: {
|
||||||
options: {
|
options: {
|
||||||
|
@ -19,26 +19,21 @@ module.exports = function(grunt) {
|
||||||
dest: "build/"
|
dest: "build/"
|
||||||
},
|
},
|
||||||
vendorjs: {
|
vendorjs: {
|
||||||
src: [ "es6-shim/es6-shim.min.js" ],
|
src: ["es6-shim/es6-shim.min.js",
|
||||||
|
"es6-shim/es6-shim.map"],
|
||||||
expand: true,
|
expand: true,
|
||||||
cwd: "bower_components/",
|
cwd: "node_modules/",
|
||||||
dest: "build/vendor/"
|
dest: "build/vendor/"
|
||||||
},
|
},
|
||||||
robotoSlab: {
|
|
||||||
src: [ "fonts/*",
|
|
||||||
"roboto-slab-fontface.css"
|
|
||||||
],
|
|
||||||
expand: true,
|
|
||||||
dest: "build/",
|
|
||||||
cwd: "bower_components/roboto-slab-fontface"
|
|
||||||
},
|
|
||||||
roboto: {
|
roboto: {
|
||||||
src: [ "fonts/*",
|
src: [ "fonts/roboto/*",
|
||||||
"roboto-fontface.css"
|
"fonts/roboto-slab/*",
|
||||||
|
"css/roboto/roboto-fontface.css",
|
||||||
|
"css/roboto-slab/roboto-slab-fontface.css"
|
||||||
],
|
],
|
||||||
expand: true,
|
expand: true,
|
||||||
dest: "build/",
|
dest: "build/",
|
||||||
cwd: "bower_components/roboto-fontface"
|
cwd: "node_modules/roboto-fontface/"
|
||||||
},
|
},
|
||||||
ionicons: {
|
ionicons: {
|
||||||
src: [ "fonts/*",
|
src: [ "fonts/*",
|
||||||
|
@ -46,53 +41,55 @@ module.exports = function(grunt) {
|
||||||
],
|
],
|
||||||
expand: true,
|
expand: true,
|
||||||
dest: "build/",
|
dest: "build/",
|
||||||
cwd: "bower_components/ionicons/"
|
cwd: "node_modules/ionicons/"
|
||||||
},
|
},
|
||||||
leafletImages: {
|
leafletImages: {
|
||||||
src: [ "images/*" ],
|
src: [ "images/*" ],
|
||||||
expand: true,
|
expand: true,
|
||||||
dest: "build/",
|
dest: "build/",
|
||||||
cwd: "bower_components/leaflet/dist/"
|
cwd: "node_modules/leaflet/dist/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sass: {
|
sass: {
|
||||||
dist: {
|
|
||||||
options: {
|
options: {
|
||||||
style: "compressed"
|
sourceMap: true,
|
||||||
|
outputStyle: "compressed",
|
||||||
|
implementation: require("dart-sass")
|
||||||
},
|
},
|
||||||
|
dist: {
|
||||||
files: {
|
files: {
|
||||||
"build/style.css": "scss/main.scss"
|
"build/style.css": "scss/main.scss"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
postcss: {
|
||||||
|
options: {
|
||||||
|
map: true,
|
||||||
|
processors: [
|
||||||
|
require("autoprefixer")({
|
||||||
|
browsers: ["last 2 versions"]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
src: "build/style.css"
|
||||||
|
}
|
||||||
|
},
|
||||||
cssmin: {
|
cssmin: {
|
||||||
target: {
|
target: {
|
||||||
files: {
|
files: {
|
||||||
"build/style.css": [ "bower_components/leaflet/dist/leaflet.css",
|
"build/style.css": [ "node_modules/leaflet/dist/leaflet.css",
|
||||||
"bower_components/Leaflet.label/dist/leaflet.label.css",
|
"node_modules/leaflet-label/dist/leaflet.label.css",
|
||||||
"style.css"
|
"style.css"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bower-install-simple": {
|
|
||||||
options: {
|
|
||||||
directory: "<%=bowerdir%>",
|
|
||||||
color: true,
|
|
||||||
interactive: false,
|
|
||||||
production: true
|
|
||||||
},
|
|
||||||
"prod": {
|
|
||||||
options: {
|
|
||||||
production: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
requirejs: {
|
requirejs: {
|
||||||
compile: {
|
compile: {
|
||||||
options: {
|
options: {
|
||||||
baseUrl: "lib",
|
baseUrl: "lib",
|
||||||
name: "../bower_components/almond/almond",
|
name: "../node_modules/almond/almond",
|
||||||
mainConfigFile: "app.js",
|
mainConfigFile: "app.js",
|
||||||
include: "../app",
|
include: "../app",
|
||||||
wrap: true,
|
wrap: true,
|
||||||
|
@ -103,8 +100,8 @@ module.exports = function(grunt) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
grunt.loadNpmTasks("grunt-bower-install-simple")
|
|
||||||
grunt.loadNpmTasks("grunt-contrib-copy")
|
grunt.loadNpmTasks("grunt-contrib-copy")
|
||||||
grunt.loadNpmTasks("grunt-contrib-requirejs")
|
grunt.loadNpmTasks("grunt-contrib-requirejs")
|
||||||
grunt.loadNpmTasks("grunt-contrib-sass")
|
grunt.loadNpmTasks("grunt-sass")
|
||||||
|
grunt.loadNpmTasks("grunt-postcss")
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,6 @@ module.exports = function (grunt) {
|
||||||
options: {
|
options: {
|
||||||
install: true
|
install: true
|
||||||
},
|
},
|
||||||
bower: {
|
|
||||||
options: {
|
|
||||||
packageManager: "bower"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
npm: {}
|
npm: {}
|
||||||
},
|
},
|
||||||
eslint: {
|
eslint: {
|
||||||
|
|
Loading…
Reference in a new issue