Compare commits

...

42 commits

Author SHA1 Message Date
Alexander d70d3d7c5f Add Node.js LTS version in travis.yml (#105) 2019-02-01 06:52:56 +01:00
Milan Pässler fe17e32886 fix: roboto and ionicons 2019-01-26 04:55:33 +01:00
Milan Pässler 420be21fa5 add version number 2019-01-24 00:26:00 +01:00
Milan Pässler a971dcfeed update installation instructions 2019-01-20 20:21:17 +01:00
Milan Pässler 0a05523dd1 remove bower and jshashes 2019-01-20 18:15:14 +01:00
Milan Pässler f416c33498 fix(travis): update nodejs, build process 2019-01-20 15:18:59 +01:00
Milan Pässler 93e0a9c758 update deps, fix build with node v10 2019-01-20 15:08:12 +01:00
anoy d231cbd5bc
Merge pull request #102 from jjx-/master
Updated DOP-NRW WMS URL
2018-08-25 18:24:55 +02:00
JJX 11368e60de
Updated DOP-NRW WMS URL
Der alte WMS Dienst für Luftbilder des Landes wird in Kürze abgeschaltet, dies ist der Neue.
2018-08-24 11:40:27 +02:00
PetaByteBoy // Milan Pässler 587740af80 change map layers in default config 2017-04-12 02:22:47 +02:00
Simon Wüllhorst 1235d8cb46 lib/map: support wms layers. (#89) 2017-04-12 01:56:05 +02:00
Andreas Ziegler 100268f3b7 change links to new upstream project name in readme & about box (#91) 2017-04-02 16:13:05 +02:00
Milan Pässler 35589eabef fix html indention 2017-03-16 23:08:49 +01:00
H4ndl3 8f7c63bbce add loading animation
Create _loader.scss

Update main.scss

Update _base.scss

Update gui.js

Update index.html

removed trailing spaces

removed trailing white spaces for real
2017-03-16 19:54:57 +01:00
Milan Paessler b2c2627d73 infobox: fix error when gateway could not be resolved 2017-02-14 21:33:36 +01:00
kb-light a7756c44e8 infobox/link: linkinfos {SOURCE_NAME} and {TARGET_NAME} support (#82) 2017-02-11 01:21:20 +01:00
Daniel Krah 5f98da2717 Update README.md (#86)
change installation of sass on mac.
Version via gem is outdated
2017-02-11 01:20:04 +01:00
PetaByteBoy // Milan Pässler 1a6a4329b5 forcegraph: only draw clients when zoomed in 2017-02-08 09:35:26 +01:00
vsandre ccc5f0f526 nodelist: Add node id to the href of node name in the table (#84)
So it is possible to open multiple node pages in a new window/tab
2017-02-03 23:02:20 +01:00
Marvin W 5cb88e8d06 Use server side resolving only, add proper support for nexthop resolving (#81)
* Revert "Use resolved data if gateway was already resolved (#78)"

This reverts commit 44bb8e9d3d.

* Revert "proportions: lookup gateway name"

This reverts commit 94662cb3dc.

* Revert "infobox/node: change mac used to lookup nodes"

This reverts commit 2ca2604403.

* Revert "infobox/node: resolve nexthop and gateway from data"

This reverts commit 9e7049c9e3.

* Resolve nexthop full chain if possible

requires hopglass/hopglass-server#79

* Correctly handle sidecase when nodes on route report inconsistent gateway
2017-01-07 18:43:04 +01:00
Marvin W 44bb8e9d3d Use resolved data if gateway was already resolved (#78)
This is now very dirty, I think the old approach was better (although it requires a server side change)
2016-12-10 11:45:21 +01:00
Jonas Platte 7e6d054e98 scss: Fix a few CSS problems (#73)
* Set text color on body

* Fix not being able to scroll more then sidebar height in Firefox

* Fix button color being set by browser stylesheet instead of site stylesheet
2016-11-26 00:10:27 +01:00
Milan Pssler 94662cb3dc proportions: lookup gateway name 2016-11-19 11:01:25 +01:00
Milan Pssler 2ca2604403 infobox/node: change mac used to lookup nodes 2016-11-19 10:57:24 +01:00
Milan Paessler 0171ebe7e1 infobox/node: non-clickable mac adresses should be black 2016-11-19 02:27:21 +01:00
Milan Paessler 9e7049c9e3 infobox/node: resolve nexthop and gateway from data
not perfect yet: the mac address doesn't always match
2016-11-19 02:17:08 +01:00
Milan Paessler 7d145141c1 infobox/node: handle unknown model when showing router img 2016-11-19 02:05:28 +01:00
Milan Pssler 5a5ce1d346 forcegraph: decrease line radius to increase click accuracy 2016-11-17 23:06:30 +01:00
Milan Pssler 224240c1c4 forcegraph: bidirectional links 2016-11-17 19:47:56 +01:00
Milan Pssler 7eb0675be0 fix meshclients for orange nodes 2016-11-06 17:13:22 +01:00
Milan Pssler 1641bc2437 router pics hopglass changes
- don't use display block on the table, instead make the close button
  position absolute
- show the old style heading when no source is defined
- clean up the indention and some small things
2016-11-06 00:11:49 +01:00
Moorviper 84aee48229 Router pics (#37)
* add a padding to sidebar for better use on mobile devices

* allow other things as text in attributeEntry

Now it is possible to use pictures and other stuff in this function

* Show Router-Pic with double fallback

3 Möglichkeiten
1. in config wird externe Quelle angegeben
2. wenn nicht wird in ./nodes/ geseucht
3. ansonsten Knotenname + Knotenname in erster Zeile

extern via:
cdn

"hwImg": [
  { "thumbnail": "https://cdn.rawgit.com/Moorviper/meshviewer_hwpics/master/nodes/{MODELHASH}.svg",
    "caption": "Knoten {MODELHASH}"
  }
 ]

aktueller nicht cdn:
"hwImg": [
  { "thumbnail": "https://rawgit.com/Moorviper/meshviewer_hwpics/master/nodes/{MODELHASH}.svg",
    "caption": "Knoten {MODELHASH}"
  }
 ]
2016-11-05 23:03:46 +01:00
Milan Pssler e9711c6efc new example config 2016-11-05 23:01:25 +01:00
Milan Pssler 5599be5cd6 re-add uplink detection, this time generic
fixes l2tp uplink detection
2016-11-05 21:54:47 +01:00
Daniel Krah 808b8c1986 Count Clients in a Mesh (#61)
This functions count all clients in a meshcloud.

It count over wireless and cable links not over VPN links via l2tp or fastd.
2016-11-05 21:24:38 +01:00
Daniel Krah 00a8e5117d fix es6-shim/es6-shim.map (#62) 2016-09-03 01:31:26 +02:00
Marvin W cb065d8d07 Fully resolve gateway names in hopglass (#57)
Depends on hopglass/hopglass-server#56
2016-08-05 19:47:25 +02:00
Marvin W bf2e858c24 Make sure hopglass does not break when server reports nodes without hostname. (#58) 2016-08-01 23:25:29 +02:00
Milan Paessler 46de672dc9 main: support for ffmap-backend vpn link notation 2016-07-23 16:33:49 +00:00
Daniel Krah 07d5e3f636 check IPv4/IPv6 (#56) 2016-07-23 14:52:49 +00:00
codedust cfd778dadb Fix number of decimal places being displayed in the CPU load bar (#54) 2016-07-23 14:49:35 +00:00
Milan Paessler 8f7b1e15ce Revert "Router pics (#37)"
This reverts commit 8435885e5c.
2016-07-04 12:45:19 +02:00
32 changed files with 3905 additions and 303 deletions

View file

@ -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

View file

@ -17,7 +17,7 @@ module.exports = function (grunt) {
grunt.loadTasks("tasks") grunt.loadTasks("tasks")
grunt.registerTask("default", ["bower-install-simple", "lint", "saveRevision", "copy", "sass", "postcss", "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"])
} }

View file

@ -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,29 +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 package-manager. On Debian-like systems run: Install npm package-manager. On Debian-like systems run:
sudo apt-get install npm sudo apt-get install nodejs
On Mac you have to install only npm via brew and sass **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 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
@ -52,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.

26
app.js
View file

@ -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"],

View file

@ -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
}

View file

@ -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,

View file

@ -1,21 +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": "CartoDB",
"url": "https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png",
"config": {
"maxZoom": 18,
"attribution": "&copy; <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>, &copy; | <a href=\"https://carto.com/attribution\">CARTO</a>"
}
},
{ {
"name": "OpenStreetMap.HOT" "name": "OpenStreetMap.HOT"
}, },
{ {
"name": "Stamen.TonerLite" "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}"
}
] ]
} }

View file

@ -132,9 +132,10 @@ function attributeEntry(el, label, value) {
var th = document.createElement("th") var th = document.createElement("th")
if (typeof label === "string") if (typeof label === "string")
th.textContent = label th.textContent = label
else {
else
th.appendChild(label) th.appendChild(label)
tr.className = "routerpic"
}
tr.appendChild(th) tr.appendChild(th)

View file

@ -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 &amp; Knoten...
</p>
<noscript>
<strong>JavaScript required</strong>
</noscript>
</div>
</body> </body>
</html> </html>

View file

@ -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>

View file

@ -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
} }

View file

@ -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
@ -242,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)
@ -324,13 +325,16 @@ 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.isVPN ? 0.1 : 0.8 ctx.globalAlpha = d.o.isVPN ? 0.1 : 0.8
ctx.lineWidth = d.o.isVPN ? 1.5 : 2.5 ctx.lineWidth = d.o.isVPN ? 1.5 : 2.5
@ -376,6 +380,7 @@ define(["d3"], function (d3) {
// -- 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)
@ -470,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) {
@ -696,6 +703,15 @@ define(["d3"], function (d3) {
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]

View file

@ -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)

View file

@ -1,8 +1,10 @@
define(function () { define(function () {
function showStatImg(o, source, target) { function showStatImg(o, d) {
var subst = {} var subst = {}
subst["{SOURCE}"] = source subst["{SOURCE}"] = d.source.node_id
subst["{TARGET}"] = target 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 showStat(o, subst)
} }
@ -35,15 +37,12 @@ define(function () {
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) { if (config.linkInfos)
var source = d.source.node_id
var target = d.target.node_id
config.linkInfos.forEach( function (linkInfo) { config.linkInfos.forEach( function (linkInfo) {
var h4 = document.createElement("h4") var h4 = document.createElement("h4")
h4.textContent = linkInfo.name h4.textContent = linkInfo.name
el.appendChild(h4) el.appendChild(h4)
el.appendChild(showStatImg(linkInfo, source, target)) el.appendChild(showStatImg(linkInfo, d))
}) })
} }
}
}) })

View file

@ -133,14 +133,72 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
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"))
} }
} }
@ -160,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)
@ -204,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
@ -237,7 +298,22 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
} }
} }
function showGateway(d) { 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 var nh
if (dictGet(d.statistics, ["nexthop"])) if (dictGet(d.statistics, ["nexthop"]))
nh = dictGet(d.statistics, ["nexthop"]) nh = dictGet(d.statistics, ["nexthop"])
@ -245,13 +321,31 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
nh = dictGet(d.statistics, ["gateway_nexthop"]) nh = dictGet(d.statistics, ["gateway_nexthop"])
var gw = dictGet(d.statistics, ["gateway"]) var gw = dictGet(d.statistics, ["gateway"])
if (gw && !nh) if (!gw) return null
return gw return function (el) {
if (gw && nh) var num = 0
if (gw === nh) while (gw && nh && gw.id !== nh.id && num < 10) {
return gw 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 else
return nh + " -> ... -> " + gw 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) {
@ -295,7 +389,7 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
function showNodeImg(o, model) { function showNodeImg(o, model) {
if (!model) if (!model)
return undefined return document.createTextNode("Knotenname")
var content, caption var content, caption
var modelhash = model.split("").reduce(function(a, b) { var modelhash = model.split("").reduce(function(a, b) {
@ -303,16 +397,13 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
return a & a return a & a
}, 0) }, 0)
if (o.thumbnail) {
content = document.createElement("img") content = document.createElement("img")
content.id = "routerpicture" content.id = "routerpicture"
content.classList.add("nodeImg") content.classList.add("nodeImg")
content.src = o.thumbnail.replace("{MODELHASH}", modelhash) content.src = o.thumbnail.replace("{MODELHASH}", modelhash)
content.onerror = function() { content.onerror = function() {
console.log("Router-Bild nicht vorhanden !!! create an issue @ https://github.com/Moorviper/Freifunk-Router-Anleitungen/issues")
document.getElementById("routerpicdiv").outerHTML = "Knotenname" document.getElementById("routerpicdiv").outerHTML = "Knotenname"
} }
}
if (o.caption) { if (o.caption) {
caption = o.caption.replace("{MODELHASH}", modelhash) caption = o.caption.replace("{MODELHASH}", modelhash)
@ -335,44 +426,30 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
} }
return function(config, el, router, d) { return function(config, el, router, d) {
var top = document.createElement("div")
top.id = "routerpicdiv"
try {
if (config.hwImg)
config.hwImg.forEach(function(hwImg) {
try {
top.appendChild(showNodeImg(hwImg, d.nodeinfo.hardware.model))
}
catch (err) {
console.log(err.message)
}
})
else
{
var localpic = [] // create fallback-config-data
localpic.push({
thumbnail: "./nodes/{MODELHASH}.svg",
caption: "Knoten {MODELHASH}"
})
localpic.forEach(function(localpic) {
try {
top.appendChild(showNodeImg(localpic, d.nodeinfo.hardware.model))
}
catch (err) {
console.log(err.message)
}
})
}
}
catch (err) {
console.log(err.message)
}
var attributes = document.createElement("table") var attributes = document.createElement("table")
attributes.classList.add("attributes") 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) attributeEntry(attributes, top, d.nodeinfo.hostname)
} else {
var h2 = document.createElement("h2")
h2.textContent = d.nodeinfo.hostname
el.appendChild(h2)
}
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)
attributeEntry(attributes, "Koordinaten", showGeoURI(d)) attributeEntry(attributes, "Koordinaten", showGeoURI(d))
@ -395,9 +472,9 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"],
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", showGateway(d)) 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)
@ -454,13 +531,7 @@ 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 = unknown ? d.id : d.node.nodeinfo.hostname
if (!unknown)
a1.href = "#"
a1.onclick = router.node(d.node)
td2.appendChild(a1)
if (!unknown && has_location(d.node)) { if (!unknown && has_location(d.node)) {
var span = document.createElement("span") var span = document.createElement("span")

View file

@ -123,10 +123,19 @@ 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") { if (d.type === "tunnel" || d.vpn) {
d.type = "VPN" d.type = "VPN"
d.isVPN = true d.isVPN = true
} else if (d.type === "fastd") { } else if (d.type === "fastd") {
@ -148,6 +157,10 @@ function (moment, Router, L, GUI, numeral) {
d.type = "N/A" d.type = "N/A"
d.isVPN = false d.isVPN = false
} }
if (d.isVPN && d.target.node)
d.target.node.flags.uplink = true
var unknown = (d.source.node === undefined) var unknown = (d.source.node === undefined)
if (unknown) { if (unknown) {
d.target.node.neighbours.push({ id: d.source.id, link: d, incoming: true }) d.target.node.neighbours.push({ id: d.source.id, link: d, incoming: true })

View file

@ -286,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)
} }
}) })

View file

@ -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()
}, },

View file

@ -13,7 +13,7 @@ define(function () {
return d.statistics.clients ? d.statistics.clients : 0 return d.statistics.clients ? d.statistics.clients : 0
})) }))
var totalGateways = sum(Array.from(new Set(d.nodes.all.filter(online).map( function(d) { var totalGateways = sum(Array.from(new Set(d.nodes.all.filter(online).map( function(d) {
return d.statistics.gateway return ("gateway" in d.statistics && d.statistics.gateway.id) ? d.statistics.gateway.id : d.statistics.gateway
}).concat(d.nodes.all.filter( function (d) { }).concat(d.nodes.all.filter( function (d) {
return d.flags.gateway return d.flags.gateway
})))).map(function(d) { })))).map(function(d) {

View file

@ -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))

View file

@ -156,8 +156,11 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
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
}) })
@ -166,8 +169,11 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
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
}) })
@ -188,8 +194,8 @@ define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "verc
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("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("Autom. Updates", autoTable, autoDict.sort(function (a, b) { return b[1] - a[1] })) fillTable("Autom. Updates", autoTable, autoDict.sort(function (a, b) { return b[1] - a[1] }))
fillTable("Nodes an Gateway", gwNodesTable, gwNodesDict.sort(function (a, b) { return b[1] - a[1] })) fillTable("Gateway", gwNodesTable, gwNodesDict.sort(function (a, b) { return b[1] - a[1] }))
fillTable("Clients an Gateway", gwClientsTable, gwClientsDict.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] }))
} }

View file

@ -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))

View file

@ -1,24 +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": {
"autoprefixer": "^6.3.3", "autoprefixer": "^6.3.3",
"grunt": "^0.4.5", "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-sass": "^1.1.0",
"grunt-postcss": "^0.7.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": {

View file

@ -28,3 +28,7 @@ h5 {
h6 { h6 {
font-size: 0.67em; font-size: 0.67em;
} }
.hide {
display: none;
}

View file

@ -1 +1 @@
../bower_components/Leaflet.label/dist/leaflet.label.css ../node_modules/leaflet-label/dist/leaflet.label.css

View file

@ -1 +1 @@
../bower_components/leaflet/dist/leaflet.css ../node_modules/leaflet/dist/leaflet.css

23
scss/_loader.scss Normal file
View 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);
}
}

View file

@ -13,6 +13,7 @@
display: block; display: block;
z-index: 2; z-index: 2;
} }
.nodeheader img .none { .nodeheader img .none {
display: none; display: none;
} }
@ -33,8 +34,3 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.limit {
min-height: 1px;
max-height: 1px;
}

View file

@ -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;
@ -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 {
@ -101,11 +103,6 @@ table th.sort-up:after {
content: '\f104'; content: '\f104';
} }
table.attributes {
top: 1px;
display: block;
}
table.attributes th { table.attributes th {
text-align: left; text-align: left;
font-weight: bold; font-weight: bold;
@ -121,7 +118,7 @@ table.attributes td {
line-height: 1.41em; line-height: 1.41em;
} }
table.attributes tr:first-child { table.attributes tr.routerpic {
max-height: 128px; max-height: 128px;
max-width: 128px; max-width: 128px;
min-width: 128px; min-width: 128px;
@ -130,14 +127,14 @@ table.attributes tr:first-child {
/*background-color: green;*/ /*background-color: green;*/
} }
table.attributes tr:first-child td{ table.attributes tr.routerpic td {
font-weight: bold; font-weight: bold;
/*background-color: red;*/ /*background-color: red;*/
font-size: large; font-size: large;
vertical-align:bottom; vertical-align:bottom;
} }
table.attributes tr:first-child th{ table.attributes tr.routerpic th {
font-weight: bold; font-weight: bold;
/*background-color: red;*/ /*background-color: red;*/
font-size: large; font-size: large;
@ -158,7 +155,6 @@ table.attributes tr:first-child th{
@include shadow(2); @include shadow(2);
background: rgba(255, 255, 255, 0.97); background: rgba(255, 255, 255, 0.97);
border-radius: 2px; border-radius: 2px;
padding-bottom: 30px;
} }
.container.hidden { .container.hidden {
@ -181,6 +177,13 @@ table.attributes tr:first-child th{
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;
@ -218,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;
@ -257,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;
@ -358,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;
} }

View file

@ -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,19 +41,20 @@ 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: {
options: { options: {
sourceMap: true, sourceMap: true,
outputStyle: "compressed" outputStyle: "compressed",
implementation: require("dart-sass")
}, },
dist: { dist: {
files: { files: {
@ -82,31 +78,18 @@ module.exports = function(grunt) {
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,
@ -117,7 +100,6 @@ 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-sass") grunt.loadNpmTasks("grunt-sass")

View file

@ -4,11 +4,6 @@ module.exports = function (grunt) {
options: { options: {
install: true install: true
}, },
bower: {
options: {
packageManager: "bower"
}
},
npm: {} npm: {}
}, },
eslint: { eslint: {

3451
yarn.lock Normal file

File diff suppressed because it is too large Load diff