Compare commits
	
		
			32 commits
		
	
	
		
			
				master
			
			...
			
				rgb-rebase
			
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | bdde93a2c8 | ||
|   | 27704d4e3e | ||
|   | 4171594782 | ||
|   | 4c26a8c2c9 | ||
|   | 9987e443f7 | ||
|   | 5c15ccf340 | ||
|   | 6869a19ea0 | ||
|   | 173674c2a1 | ||
|   | c451775021 | ||
|   | 09714f3b58 | ||
|   | d4670d8742 | ||
|   | fe3ba88887 | ||
|   | 2738967343 | ||
|   | d5aa447ab8 | ||
|   | 41c6a03e6b | ||
|   | f1e9aacdf7 | ||
|   | 8520abf676 | ||
|   | 6908e2953f | ||
|   | 136157eb37 | ||
|   | d367bf58bf | ||
|   | b510dda006 | ||
|   | 7f3a86596a | ||
|   | 231bfcedd7 | ||
|   | 88f43bc57c | ||
|   | 8bb0da9d87 | ||
|   | 44571c51f0 | ||
|   | 30b6f84b6d | ||
|   | f7f5744a78 | ||
|   | 0a22ed5e6f | ||
|   | 9afb214360 | ||
|   | eb4c7a04b0 | ||
|   | 8b5bcbfede | 
					 57 changed files with 4072 additions and 6785 deletions
				
			
		|  | @ -11,6 +11,6 @@ trim_trailing_whitespace = true | ||||||
| end_of_line = lf | end_of_line = lf | ||||||
| insert_final_newline = true | insert_final_newline = true | ||||||
| 
 | 
 | ||||||
| [*.{js,html,scss,json}] | [*.{js,html,scss,json,yml,md}] | ||||||
| indent_size = 2 | indent_size = 2 | ||||||
| indent_style = space | indent_style = space | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								.eslintrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.eslintrc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | --- | ||||||
|  | "extends": | ||||||
|  |   - "defaults/configurations/eslint" | ||||||
|  | 
 | ||||||
|  | rules: | ||||||
|  |   "semi": ["error", "always"] | ||||||
|  |   "no-undef": 0 | ||||||
|  |   "no-console": ["error", { allow: ["warn", "error"] }] | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -3,3 +3,4 @@ node_modules/ | ||||||
| build/ | build/ | ||||||
| .sass-cache/ | .sass-cache/ | ||||||
| config.json | config.json | ||||||
|  | .idea/ | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								.travis.yml
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								.travis.yml
									
										
									
									
									
								
							|  | @ -1,7 +1,8 @@ | ||||||
| language: node_js | language: node_js | ||||||
| node_js: | node_js: node | ||||||
|   - "lts/*" | before_install: | ||||||
|   - "node" |   - gem install sass | ||||||
|  |   - npm install -g grunt-cli | ||||||
| install: | install: | ||||||
|   - yarn |   - npm install | ||||||
| script: node_modules/.bin/grunt | script: grunt | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								Gruntfile.js
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								Gruntfile.js
									
										
									
									
									
								
							|  | @ -1,24 +1,11 @@ | ||||||
| module.exports = function (grunt) { | module.exports = function (grunt) { | ||||||
|   grunt.loadNpmTasks("grunt-git-describe") |   "use strict"; | ||||||
| 
 | 
 | ||||||
|   grunt.initConfig({ |   grunt.loadTasks("tasks"); | ||||||
|     "git-describe": { |  | ||||||
|       options: {}, |  | ||||||
|       default: {} |  | ||||||
|     } |  | ||||||
|   }) |  | ||||||
| 
 | 
 | ||||||
|   grunt.registerTask("saveRevision", function() { |   grunt.registerTask("default", ["bower-install-simple", "lint", "copy", "sass", "postcss", "requirejs:default", "inline"]); | ||||||
|     grunt.event.once("git-describe", function (rev) { |   grunt.registerTask("lint", ["eslint"]); | ||||||
|       grunt.option("gitRevision", rev) |   grunt.registerTask("dev", ["bower-install-simple", "lint", "copy", "sass", "requirejs:dev"]); | ||||||
|     }) |   grunt.registerTask("serve", ["dev", "connect:server", "watch"]); | ||||||
|     grunt.task.run("git-describe") | }; | ||||||
|   }) |  | ||||||
| 
 |  | ||||||
|   grunt.loadTasks("tasks") |  | ||||||
| 
 |  | ||||||
|   grunt.registerTask("default", ["lint", "saveRevision", "copy", "sass", "postcss", "requirejs"]) |  | ||||||
|   grunt.registerTask("lint", ["eslint"]) |  | ||||||
|   grunt.registerTask("dev", ["default", "connect:server", "watch"]) |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										33
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,8 +1,8 @@ | ||||||
| [](https://travis-ci.org/hopglass/hopglass) | [](https://travis-ci.org/plumpudding/hopglass) | ||||||
| 
 | 
 | ||||||
| # HopGlass | # HopGlass | ||||||
| 
 | 
 | ||||||
| HopGlass is a frontend for the [HopGlass Server](https://github.com/hopglass/hopglass-server). | HopGlass is a frontend for the [HopGlass Server](https://github.com/plumpudding/hopglass-server). | ||||||
| 
 | 
 | ||||||
| # Screenshots | # Screenshots | ||||||
| 
 | 
 | ||||||
|  | @ -14,32 +14,29 @@ HopGlass is a frontend for the [HopGlass Server](https://github.com/hopglass/hop | ||||||
| 
 | 
 | ||||||
| # Dependencies | # Dependencies | ||||||
| 
 | 
 | ||||||
| - NodeJS | - npm | ||||||
| - yarn (recommended) or npm | - bower | ||||||
|  | - 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 nodejs |     sudo apt-get install npm | ||||||
| 
 | 
 | ||||||
| **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 | ||||||
|     brew install yarn |     npm install node-sass | ||||||
| 
 |  | ||||||
| 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/hopglass/hopglass |     git clone https://github.com/plumpudding/hopglass | ||||||
|     cd hopglass |     cd hopglass | ||||||
|     yarn install # or `npm install` |     npm install | ||||||
|  |     npm install grunt-cli | ||||||
| 
 | 
 | ||||||
| # Building | # Building | ||||||
| 
 | 
 | ||||||
|  | @ -49,13 +46,17 @@ Just run the following command from the hopglass directory: | ||||||
| 
 | 
 | ||||||
| This will generate `build/` containing all required files. | This will generate `build/` containing all required files. | ||||||
| 
 | 
 | ||||||
|  | ## Development | ||||||
|  | 
 | ||||||
|  | Use `grunt serve` for development. | ||||||
|  | 
 | ||||||
| # Configure | # Configure | ||||||
| 
 | 
 | ||||||
| Copy `config.json.example` to `build/config.json` and change it to match your community. | Copy `config.json.example` to `build/config.json` and change it to match your community. | ||||||
| 
 | 
 | ||||||
| ## dataPath (string/array) | ## dataPath (string/array) | ||||||
| 
 | 
 | ||||||
| `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. | `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. | ||||||
| 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. | ||||||
|  |  | ||||||
							
								
								
									
										44
									
								
								app.js
									
										
									
									
									
								
							
							
						
						
									
										44
									
								
								app.js
									
										
									
									
									
								
							|  | @ -1,36 +1,30 @@ | ||||||
|  | "use strict"; | ||||||
|  | 
 | ||||||
| require.config({ | require.config({ | ||||||
|   baseUrl: "lib", |   baseUrl: "lib", | ||||||
|   paths: { |   paths: { | ||||||
|     "leaflet": "../node_modules/leaflet/dist/leaflet", |     "leaflet": "../bower_components/leaflet/dist/leaflet", | ||||||
|     "leaflet.label": "../node_modules/leaflet-label/dist/leaflet.label", |     "leaflet.label": "../bower_components/Leaflet.label/dist/leaflet.label", | ||||||
|     "leaflet.providers": "../node_modules/leaflet-providers/leaflet-providers", |     "leaflet.providers": "../bower_components/leaflet-providers/leaflet-providers", | ||||||
|     "chroma-js": "../node_modules/chroma-js/chroma.min", |     "chroma-js": "../bower_components/chroma-js/chroma.min", | ||||||
|     "moment": "../node_modules/moment/min/moment-with-locales.min", |     "moment": "../bower_components/moment/min/moment.min", | ||||||
|     "tablesort": "../node_modules/tablesort/tablesort.min", |     "moment.de": "../bower_components/moment/locale/de", | ||||||
|     "tablesort.numeric": "../node_modules/tablesort/src/sorts/tablesort.numeric", |     "tablesort": "../bower_components/tablesort/src/tablesort", | ||||||
|     "d3": "../node_modules/d3/d3.min", |     "d3": "../bower_components/d3/d3.min", | ||||||
|     "numeral": "../node_modules/numeraljs/min/numeral.min", |     "virtual-dom": "../bower_components/virtual-dom/dist/virtual-dom", | ||||||
|     "numeral-intl": "../node_modules/numeraljs/min/languages.min", |     "rbush": "../bower_components/rbush/rbush", | ||||||
|     "virtual-dom": "../node_modules/virtual-dom/dist/virtual-dom", |     "helper": "utils/helper" | ||||||
|     "rbush": "../node_modules/rbush/rbush", |  | ||||||
|     "helper": "../helper", |  | ||||||
|     "jshashes": "../node_modules/jshashes/hashes" |  | ||||||
|   }, |   }, | ||||||
|   shim: { |   shim: { | ||||||
|     "leaflet.label": ["leaflet"], |     "leaflet.label": ["leaflet"], | ||||||
|     "leaflet.providers": ["leaflet"], |     "leaflet.providers": ["leaflet"], | ||||||
|  |     "moment.de": ["moment"], | ||||||
|     "tablesort": { |     "tablesort": { | ||||||
|       exports: "Tablesort" |       exports: "Tablesort" | ||||||
|     }, |  | ||||||
|     "numeral-intl": { |  | ||||||
|       deps: ["numeral"], |  | ||||||
|       exports: "numeral" |  | ||||||
|     }, |  | ||||||
|     "tablesort.numeric": ["tablesort"], |  | ||||||
|     "helper": ["numeral-intl"] |  | ||||||
|     } |     } | ||||||
| }) |   } | ||||||
|  | }); | ||||||
| 
 | 
 | ||||||
| require(["main", "helper"], function (main) { | require(["main", "helper"], function (main, helper) { | ||||||
|   getJSON("config.json").then(main) |   helper.getJSON("config.json").then(main); | ||||||
| }) | }); | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								assets/icons/fonts/icon.ttf
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/fonts/icon.ttf
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								assets/icons/fonts/icon.woff
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/fonts/icon.woff
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								assets/icons/fonts/icon.woff2
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/fonts/icon.woff2
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										96
									
								
								assets/icons/hopglass-icons.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								assets/icons/hopglass-icons.css
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | ||||||
|  | @font-face { | ||||||
|  |     font-family: "Ionicons"; | ||||||
|  |     src: url("fonts/icon.woff2") format("woff2"), | ||||||
|  |     url("fonts/icon.woff") format("woff"), | ||||||
|  |     url("fonts/icon.ttf") format("truetype"); | ||||||
|  |     font-weight: normal; | ||||||
|  |     font-style: normal; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | [class^="ion-"]:before, | ||||||
|  | [class*=" ion-"]:before, .ion-inside { | ||||||
|  |     display: inline-block; | ||||||
|  |     font-family: "ionicons" !important; | ||||||
|  |     speak: none; | ||||||
|  |     font-style: normal; | ||||||
|  |     font-weight: normal; | ||||||
|  |     font-variant: normal; | ||||||
|  |     text-transform: none; | ||||||
|  |     text-rendering: auto; | ||||||
|  |     line-height: 1; | ||||||
|  |     -webkit-font-smoothing: antialiased; | ||||||
|  |     -moz-osx-font-smoothing: grayscale; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-android-add:before { | ||||||
|  |     content: "\f2c7"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-chevron-left:before { | ||||||
|  |     content: "\f124"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-chevron-right:before { | ||||||
|  |     content: "\f125"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-pin:before { | ||||||
|  |     content: "\f3a3"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-wifi:before { | ||||||
|  |     content: "\f25c"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-eye:before { | ||||||
|  |     content: "\f133"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-ios-arrow-thin-left:before { | ||||||
|  |     content: "\f3d5"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-ios-arrow-thin-right:before { | ||||||
|  |     content: "\f3d6"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-arrow-up-b:before { | ||||||
|  |     content: "\f10d"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-arrow-down-b:before { | ||||||
|  |     content: "\f104"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-android-locate:before { | ||||||
|  |     content: "\f2e9"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-android-close:before { | ||||||
|  |     content: "\f2d7"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-android-lock:before { | ||||||
|  |     content: "\f392"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-ios-copy:before { | ||||||
|  |     content: "\f41c"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-location:before { | ||||||
|  |     content: "\f456"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-android-remove:before { | ||||||
|  |     content: "\f2f4"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-ios-person:before { | ||||||
|  |     content: "\f47e"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ion-layer:before { | ||||||
|  |     content: "\f229"; | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								bower.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								bower.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | { | ||||||
|  |   "name": "hopglass", | ||||||
|  |   "ignore": [ | ||||||
|  |     "node_modules", | ||||||
|  |     "bower_components", | ||||||
|  |     "**/.*", | ||||||
|  |     "test", | ||||||
|  |     "tests" | ||||||
|  |   ], | ||||||
|  |   "dependencies": { | ||||||
|  |     "Leaflet.label": "~0.2.1", | ||||||
|  |     "chroma-js": "~1.1.1", | ||||||
|  |     "leaflet": "~0.7.7", | ||||||
|  |     "moment": "~2.13.0", | ||||||
|  |     "requirejs": "~2.2.0", | ||||||
|  |     "tablesort": "https://github.com/tristen/tablesort.git#v4.0.1", | ||||||
|  |     "roboto-slab-fontface": "*", | ||||||
|  |     "es6-shim": "~0.35.1", | ||||||
|  |     "almond": "~0.3.2", | ||||||
|  |     "d3": "~3.5.17", | ||||||
|  |     "roboto-fontface": "~0.4.5", | ||||||
|  |     "virtual-dom": "~2.1.1", | ||||||
|  |     "leaflet-providers": "~1.1.10", | ||||||
|  |     "rbush": "https://github.com/mourner/rbush.git#~1.4.3" | ||||||
|  |   }, | ||||||
|  |   "authors": [ | ||||||
|  |     "Milan Pässler <me@petabyteboy.de>", | ||||||
|  |     "Nils Schneider <nils@nilsschneider.net>" | ||||||
|  |   ], | ||||||
|  |   "license": "AGPL3", | ||||||
|  |   "private": true | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								build.js
									
										
									
									
									
								
							
							
						
						
									
										9
									
								
								build.js
									
										
									
									
									
								
							|  | @ -1,9 +0,0 @@ | ||||||
| ({ |  | ||||||
|     baseUrl: "lib", |  | ||||||
|     name: "../node_modules/almond/almond", |  | ||||||
|     mainConfigFile: "app.js", |  | ||||||
|     include: "../app", |  | ||||||
|     wrap: true, |  | ||||||
|     optimize: "uglify", |  | ||||||
|     out: "app-combined.js" |  | ||||||
| }) |  | ||||||
|  | @ -5,25 +5,11 @@ | ||||||
|   "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": "© <a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>, © | <a href=\"https://carto.com/attribution\">CARTO</a>" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     { |     { | ||||||
|       "name": "OpenStreetMap.HOT" |       "name": "OpenStreetMap.HOT" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "name": "Luftbilder NRW", |       "name": "Stamen.TonerLite" | ||||||
|       "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": [ |   "nodeInfos": [ | ||||||
|  |  | ||||||
							
								
								
									
										233
									
								
								helper.js
									
										
									
									
									
								
							
							
						
						
									
										233
									
								
								helper.js
									
										
									
									
									
								
							|  | @ -1,228 +1,241 @@ | ||||||
| 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) { | ||||||
|   return get(url).then(JSON.parse) |   return get(url).then(JSON.parse); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function sortByKey(key, d) { | function sortByKey(key, d) { | ||||||
|   return d.slice().sort(function (a, b) { |   return d.slice().sort(function (a, b) { | ||||||
|     return a[key] - b[key] |     return a[key] - b[key]; | ||||||
|   }).reverse() |   }).reverse(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function limit(key, m, d) { | function limit(key, m, d) { | ||||||
|   return d.filter(function (d) { |   return d.filter(function (d) { | ||||||
|     return d[key].isAfter(m) |     return d[key].isAfter(m); | ||||||
|   }) |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function sum(a) { | function sum(a) { | ||||||
|   return a.reduce(function (a, b) { |   return a.reduce(function (a, b) { | ||||||
|     return a + b |     return a + b; | ||||||
|   }, 0) |   }, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function one() { | function one() { | ||||||
|   return 1 |   return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function trueDefault(d) { | function trueDefault(d) { | ||||||
|   return d === undefined ? true : d |   return d === undefined ? true : d; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function dictGet(dict, key) { | function dictGet(dict, key) { | ||||||
|   var k = key.shift() |   var k = key.shift(); | ||||||
| 
 | 
 | ||||||
|   if (!(k in dict)) |   if (!(k in dict)) { | ||||||
|     return null |     return null; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   if (key.length == 0) |   if (key.length == 0) { | ||||||
|     return dict[k] |     return dict[k]; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   return dictGet(dict[k], key) |   return dictGet(dict[k], key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function localStorageTest() { | function localStorageTest() { | ||||||
|   var test = 'test' |   var test = 'test'; | ||||||
|   try { |   try { | ||||||
|     localStorage.setItem(test, test) |     localStorage.setItem(test, test); | ||||||
|     localStorage.removeItem(test) |     localStorage.removeItem(test); | ||||||
|     return true |     return true; | ||||||
|   } catch (e) { |   } catch (e) { | ||||||
|     return false |     return false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function listReplace(s, subst) { | function listReplace(s, subst) { | ||||||
|   for (key in subst) { |   for (key in subst) { | ||||||
|     var re = new RegExp(key, 'g') |     var re = new RegExp(key, 'g'); | ||||||
|     s = s.replace(re, subst[key]) |     s = s.replace(re, subst[key]); | ||||||
|   } |   } | ||||||
|   return s |   return s; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Helpers working with nodes */ | /* Helpers working with nodes */ | ||||||
| 
 | 
 | ||||||
| function offline(d) { | function offline(d) { | ||||||
|   return !d.flags.online |   return !d.flags.online; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function online(d) { | function online(d) { | ||||||
|   return d.flags.online |   return d.flags.online; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function has_location(d) { | function has_location(d) { | ||||||
|   return "location" in d.nodeinfo && |   return "location" in d.nodeinfo && | ||||||
|     Math.abs(d.nodeinfo.location.latitude) < 90 && |     Math.abs(d.nodeinfo.location.latitude) < 90 && | ||||||
|          Math.abs(d.nodeinfo.location.longitude) < 180 |     Math.abs(d.nodeinfo.location.longitude) < 180; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function subtract(a, b) { | function subtract(a, b) { | ||||||
|   var ids = {} |   var ids = {}; | ||||||
| 
 | 
 | ||||||
|   b.forEach(function (d) { |   b.forEach(function (d) { | ||||||
|     ids[d.nodeinfo.node_id] = true |     ids[d.nodeinfo.node_id] = true; | ||||||
|   }) |   }); | ||||||
| 
 | 
 | ||||||
|   return a.filter(function (d) { |   return a.filter(function (d) { | ||||||
|     return !(d.nodeinfo.node_id in ids) |     return !(d.nodeinfo.node_id in ids); | ||||||
|   }) |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Helpers working with links */ | /* Helpers working with links */ | ||||||
| 
 | 
 | ||||||
| function showDistance(d) { | function showDistance(d) { | ||||||
|   if (isNaN(d.distance)) |   if (isNaN(d.distance)) { | ||||||
|     return |     return; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   return numeral(d.distance).format("0,0") + " m" |   return d.distance.toFixed(0) + " m" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function showTq(d) { | function showTq(d) { | ||||||
|   return numeral(1/d.tq).format("0%") |   return (1 / d.tq * 100).toFixed(0) + "%" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Infobox stuff (XXX: move to module) */ | /* Infobox stuff (XXX: move to module) */ | ||||||
| 
 | 
 | ||||||
| function attributeEntry(el, label, value) { | function attributeEntry(el, label, value) { | ||||||
|   if (value === null || value == undefined) |   if (value === null || value == undefined) { | ||||||
|     return |     return; | ||||||
| 
 |  | ||||||
|   var tr = document.createElement("tr") |  | ||||||
|   var th = document.createElement("th") |  | ||||||
|   if (typeof label === "string") |  | ||||||
|     th.textContent = label |  | ||||||
|   else { |  | ||||||
|     th.appendChild(label) |  | ||||||
|     tr.className = "routerpic" |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   tr.appendChild(th) |   var tr = document.createElement("tr"); | ||||||
|  |   var th = document.createElement("th"); | ||||||
|  |   if (typeof label === "string") { | ||||||
|  |     th.textContent = label; | ||||||
|  |   } else { | ||||||
|  |     th.appendChild(label); | ||||||
|  |     tr.className = "routerpic"; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   var td = document.createElement("td") |   tr.appendChild(th); | ||||||
| 
 | 
 | ||||||
|   if (typeof value == "function") |   var td = document.createElement("td"); | ||||||
|     value(td) |  | ||||||
|   else |  | ||||||
|     td.appendChild(document.createTextNode(value)) |  | ||||||
| 
 | 
 | ||||||
|   tr.appendChild(td) |   if (typeof value == "function") { | ||||||
|  |     value(td); | ||||||
|  |   } else { | ||||||
|  |     td.appendChild(document.createTextNode(value)); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   el.appendChild(tr) |   tr.appendChild(td); | ||||||
| 
 | 
 | ||||||
|   return td |   el.appendChild(tr); | ||||||
|  | 
 | ||||||
|  |   return td; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function createIframe(opt, width, height) { | function createIframe(opt, width, height) { | ||||||
|   el = document.createElement("iframe") |   el = document.createElement("iframe"); | ||||||
|   width = typeof width !== 'undefined' ? width : '100%' |   width = typeof width !== 'undefined' ? width : '100%'; | ||||||
|   height = typeof height !== 'undefined' ? height : '350px' |   height = typeof height !== 'undefined' ? height : '350px'; | ||||||
| 
 | 
 | ||||||
|   if (opt.src) |   if (opt.src) { | ||||||
|     el.src = opt.src |     el.src = opt.src; | ||||||
|   else |   } else { | ||||||
|     el.src = opt |     el.src = opt; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   if (opt.frameBorder) |   if (opt.frameBorder) { | ||||||
|     el.frameBorder = opt.frameBorder |     el.frameBorder = opt.frameBorder; | ||||||
|   else |   } else { | ||||||
|     el.frameBorder = 1 |     el.frameBorder = 1; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   if (opt.width) |   if (opt.width) { | ||||||
|     el.width = opt.width |     el.width = opt.width; | ||||||
|   else |   } else { | ||||||
|     el.width = width |     el.width = width; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   if (opt.height) |   if (opt.height) { | ||||||
|     el.height = opt.height |     el.height = opt.height; | ||||||
|   else |   } else { | ||||||
|     el.height = height |     el.height = height; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   el.scrolling = "no" |   el.scrolling = "no"; | ||||||
|   el.seamless = "seamless" |   el.seamless = "seamless"; | ||||||
| 
 | 
 | ||||||
|   return el |   return el; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function showStat(o, subst) { | function showStat(o, subst) { | ||||||
|   var content, caption |   var content, caption; | ||||||
|   subst = typeof subst !== 'undefined' ? subst : {} |   subst = typeof subst !== 'undefined' ? subst : {}; | ||||||
| 
 | 
 | ||||||
|   if (o.thumbnail) { |   if (o.thumbnail) { | ||||||
|     content = document.createElement("img") |     content = document.createElement("img"); | ||||||
|     content.src = listReplace(o.thumbnail, subst) |     content.src = listReplace(o.thumbnail, subst); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (o.caption) { |   if (o.caption) { | ||||||
|     caption = listReplace(o.caption, subst) |     caption = listReplace(o.caption, subst); | ||||||
| 
 | 
 | ||||||
|     if (!content) |     if (!content) { | ||||||
|     content = document.createTextNode(caption) |       content = document.createTextNode(caption); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (o.iframe) { |   if (o.iframe) { | ||||||
|     content = createIframe(o.iframe, o.width, o.height) |     content = createIframe(o.iframe, o.width, o.height); | ||||||
|     if (o.iframe.src) |     if (o.iframe.src) { | ||||||
|     content.src = listReplace(o.iframe.src, subst) |       content.src = listReplace(o.iframe.src, subst); | ||||||
|     else |     } else { | ||||||
|     content.src = listReplace(o.iframe, subst) |       content.src = listReplace(o.iframe, subst); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   var p = document.createElement("p") |   var p = document.createElement("p"); | ||||||
| 
 | 
 | ||||||
|   if (o.href) { |   if (o.href) { | ||||||
|     var link = document.createElement("a") |     var link = document.createElement("a"); | ||||||
|     link.target = "_blank" |     link.target = "_blank"; | ||||||
|     link.href = listReplace(o.href, subst) |     link.href = listReplace(o.href, subst); | ||||||
|     link.appendChild(content) |     link.appendChild(content); | ||||||
| 
 | 
 | ||||||
|     if (caption && o.thumbnail) |     if (caption && o.thumbnail) { | ||||||
|     link.title = caption |       link.title = caption; | ||||||
| 
 |     } | ||||||
|     p.appendChild(link) | 
 | ||||||
|   } else |     p.appendChild(link); | ||||||
|     p.appendChild(content) |   } else { | ||||||
| 
 |     p.appendChild(content); | ||||||
|   return p |   } | ||||||
|  | 
 | ||||||
|  |   return p; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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="css/ionicons.min.css"> |   <link rel="stylesheet" href="hopglass-icons.css?__inline=true"> | ||||||
|     <link rel="stylesheet" href="css/roboto-slab/roboto-slab-fontface.css"> |   <link rel="stylesheet" href="roboto-slab-fontface.css"> | ||||||
|     <link rel="stylesheet" href="css/roboto/roboto-fontface.css"> |   <link rel="stylesheet" href="roboto-fontface.css"> | ||||||
|     <link rel="stylesheet" href="style.css"> |   <link rel="stylesheet" href="style.css?__inline=true"> | ||||||
|     <script src="vendor/es6-shim/es6-shim.min.js"></script> |   <script src="vendor/es6-shim/es6-shim.min.js?__inline=true"></script> | ||||||
|   <script src="app.js"></script> |   <script src="app.js"></script> | ||||||
|   <script> |   <script> | ||||||
|       console.log("Version: #revision#") |     console.log("Version: #revision#"); | ||||||
|   </script> |   </script> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  |  | ||||||
							
								
								
									
										24
									
								
								index.html
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								index.html
									
										
									
									
									
								
							|  | @ -3,15 +3,25 @@ | ||||||
| <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="node_modules/roboto-fontface/css/roboto-slab/roboto-slab-fontface.css"> |   <link rel="stylesheet" href="bower_components/roboto-slab-fontface/roboto-slab-fontface.css"> | ||||||
|     <link rel="stylesheet" href="node_modules/roboto-fontface/css/roboto/roboto-fontface.css"> |   <link rel="stylesheet" href="bower_components/roboto-fontface/roboto-fontface.css"> | ||||||
|     <link rel="stylesheet" href="node_modules/leaflet/dist/leaflet.css"> |   <link rel="stylesheet" href="bower_components/leaflet/dist/leaflet.css"> | ||||||
|     <link rel="stylesheet" href="node_modules/leaflet-label/dist/leaflet.label.css"> |   <link rel="stylesheet" href="bower_components/Leaflet.label/dist/leaflet.label.css"> | ||||||
|     <link rel="stylesheet" href="node_modules/ionicons/css/ionicons.min.css"> |   <link rel="stylesheet" href="assets/icons/hopglass-icons.css"> | ||||||
|   <link rel="stylesheet" href="style.css"> |   <link rel="stylesheet" href="style.css"> | ||||||
|     <script src="node_modules/es6-shim/es6-shim.min.js"></script> |   <script src="bower_components/es6-shim/es6-shim.min.js"></script> | ||||||
|     <script src="node_modules/requirejs/require.js" data-main="app"></script> |   <script src="bower_components/requirejs/require.js" data-main="app"></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> | ||||||
|  |  | ||||||
							
								
								
									
										56
									
								
								lib/about.js
									
										
									
									
									
								
							
							
						
						
									
										56
									
								
								lib/about.js
									
										
									
									
									
								
							|  | @ -1,38 +1,40 @@ | ||||||
| define(function () { | define(function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function () { |   return function () { | ||||||
|     this.render = function (d) { |     this.render = function (d) { | ||||||
|       var el = document.createElement("div") |       var el = document.createElement("div"); | ||||||
|       d.appendChild(el) |       d.appendChild(el); | ||||||
|       var s = "<h2>Über HopGlass</h2>" |       var s = "<h2>Über HopGlass</h2>"; | ||||||
| 
 | 
 | ||||||
|       s += "<p>Mit Doppelklick und Shift+Doppelklick kann man in der Karte " |       s += "<p>Mit Doppelklick und Shift+Doppelklick kann man in der Karte "; | ||||||
|       s += "auch zoomen.</p>" |       s += "auch zoomen.</p>"; | ||||||
| 
 | 
 | ||||||
|       s += "<h3>AGPL 3</h3>" |       s += "<h3>AGPL 3</h3>"; | ||||||
| 
 | 
 | ||||||
|       s += "<p>Copyright (C) Milan Pässler</p>" |       s += "<p>Copyright (C) Milan Pässler</p>"; | ||||||
|       s += "<p>Copyright (C) Nils Schneider</p>" |       s += "<p>Copyright (C) Nils Schneider</p>"; | ||||||
| 
 | 
 | ||||||
|       s += "<p>This program is free software: you can redistribute it and/or " |       s += "<p>This program is free software: you can redistribute it and/or "; | ||||||
|       s += "modify it under the terms of the GNU Affero General Public " |       s += "modify it under the terms of the GNU Affero General Public "; | ||||||
|       s += "License as published by the Free Software Foundation, either " |       s += "License as published by the Free Software Foundation, either "; | ||||||
|       s += "version 3 of the License, or (at your option) any later version.</p>" |       s += "version 3 of the License, or (at your option) any later version.</p>"; | ||||||
| 
 | 
 | ||||||
|       s += "<p>This program is distributed in the hope that it will be useful, " |       s += "<p>This program is distributed in the hope that it will be useful, "; | ||||||
|       s += "but WITHOUT ANY WARRANTY; without even the implied warranty of " |       s += "but WITHOUT ANY WARRANTY; without even the implied warranty of "; | ||||||
|       s += "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the " |       s += "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "; | ||||||
|       s += "GNU Affero General Public License for more details.</p>" |       s += "GNU Affero General Public License for more details.</p>"; | ||||||
| 
 | 
 | ||||||
|       s += "<p>You should have received a copy of the GNU Affero General " |       s += "<p>You should have received a copy of the GNU Affero General "; | ||||||
|       s += "Public License along with this program. If not, see " |       s += "Public License along with this program. If not, see "; | ||||||
|       s += "<a href=\"https://www.gnu.org/licenses/\">" |       s += "<a href=\"https://www.gnu.org/licenses/\">"; | ||||||
|       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/hopglass/hopglass\">" |       s += "<a href=\"https://github.com/plumpudding/hopglass\">"; | ||||||
|       s += "https://github.com/hopglass/hopglass</a>." |       s += "https://github.com/plumpudding/hopglass</a>."; | ||||||
| 
 | 
 | ||||||
|       el.innerHTML = s |       el.innerHTML = s; | ||||||
|     } |     }; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,20 +1,23 @@ | ||||||
| define([], function () { | define([], function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (tag) { |   return function (tag) { | ||||||
|     if (!tag) |     if (!tag) { | ||||||
|       tag = "div" |       tag = "div"; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     var self = this |     var self = this; | ||||||
| 
 | 
 | ||||||
|     var container = document.createElement(tag) |     var container = document.createElement(tag); | ||||||
| 
 | 
 | ||||||
|     self.add = function (d) { |     self.add = function (d) { | ||||||
|       d.render(container) |       d.render(container); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.render = function (el) { |     self.render = function (el) { | ||||||
|       el.appendChild(container) |       el.appendChild(container); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     return self |     return self; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,80 +1,93 @@ | ||||||
| define(["filters/nodefilter"], function (NodeFilter) { | define(["filters/nodefilter"], function (NodeFilter) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function () { |   return function () { | ||||||
|     var targets = [] |     var targets = []; | ||||||
|     var filterObservers = [] |     var filterObservers = []; | ||||||
|     var filters = [] |     var filters = []; | ||||||
|     var filteredData |     var filteredData; | ||||||
|     var data |     var data; | ||||||
| 
 | 
 | ||||||
|     function remove(d) { |     function remove(d) { | ||||||
|       targets = targets.filter( function (e) { return d !== e } ) |       targets = targets.filter(function (e) { | ||||||
|  |         return d !== e; | ||||||
|  |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function add(d) { |     function add(d) { | ||||||
|       targets.push(d) |       targets.push(d); | ||||||
| 
 | 
 | ||||||
|       if (filteredData !== undefined) |       if (filteredData !== undefined) { | ||||||
|         d.setData(filteredData) |         d.setData(filteredData); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function setData(d) { |     function setData(d) { | ||||||
|       data = d |       data = d; | ||||||
|       refresh() |       refresh(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function refresh() { |     function refresh() { | ||||||
|       if (data === undefined) |       if (data === undefined) { | ||||||
|         return |         return; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       var filter = filters.reduce(function (a, f) { |       var filter = filters.reduce(function (a, f) { | ||||||
|         return function (d) { |         return function (d) { | ||||||
|           return a(d) && f.run(d) |           return a(d) && f.run(d); | ||||||
|         } |         }; | ||||||
|       }, function () { return true }) |       }, function () { | ||||||
|  |         return true; | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|       filteredData = new NodeFilter(filter)(data) |       filteredData = new NodeFilter(filter)(data); | ||||||
| 
 | 
 | ||||||
|       targets.forEach(function (t) { |       targets.forEach(function (t) { | ||||||
|         t.setData(filteredData) |         t.setData(filteredData); | ||||||
|       }) |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function notifyObservers() { |     function notifyObservers() { | ||||||
|       filterObservers.forEach(function (d) { |       filterObservers.forEach(function (d) { | ||||||
|         d.filtersChanged(filters) |         d.filtersChanged(filters); | ||||||
|       }) |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function addFilter(d) { |     function addFilter(d) { | ||||||
|       filters.push(d) |       filters.push(d); | ||||||
|       notifyObservers() |       notifyObservers(); | ||||||
|       d.setRefresh(refresh) |       d.setRefresh(refresh); | ||||||
|       refresh() |       refresh(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function removeFilter(d) { |     function removeFilter(d) { | ||||||
|       filters = filters.filter( function (e) { return d !== e } ) |       filters = filters.filter(function (e) { | ||||||
|       notifyObservers() |         return d !== e; | ||||||
|       refresh() |       }); | ||||||
|  |       notifyObservers(); | ||||||
|  |       refresh(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function watchFilters(d) { |     function watchFilters(d) { | ||||||
|       filterObservers.push(d) |       filterObservers.push(d); | ||||||
| 
 | 
 | ||||||
|       d.filtersChanged(filters) |       d.filtersChanged(filters); | ||||||
| 
 | 
 | ||||||
|       return function () { |       return function () { | ||||||
|         filterObservers = filterObservers.filter( function (e) { return d !== e }) |         filterObservers = filterObservers.filter(function (e) { | ||||||
|       } |           return d !== e; | ||||||
|  |         }); | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return { add: add, |     return { | ||||||
|  |       add: add, | ||||||
|       remove: remove, |       remove: remove, | ||||||
|       setData: setData, |       setData: setData, | ||||||
|       addFilter: addFilter, |       addFilter: addFilter, | ||||||
|       removeFilter: removeFilter, |       removeFilter: removeFilter, | ||||||
|       watchFilters: watchFilters, |       watchFilters: watchFilters, | ||||||
|       refresh: refresh |       refresh: refresh | ||||||
|            } |     }; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,40 +1,45 @@ | ||||||
| define([], function () { | define(function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (distributor) { |   return function (distributor) { | ||||||
|     var container = document.createElement("ul") |     var container = document.createElement("ul"); | ||||||
|     container.classList.add("filters") |     container.classList.add("filters"); | ||||||
|     var div = document.createElement("div") |     var div = document.createElement("div"); | ||||||
| 
 | 
 | ||||||
|     function render(el) { |     function render(el) { | ||||||
|       el.appendChild(div) |       el.appendChild(div); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function filtersChanged(filters) { |     function filtersChanged(filters) { | ||||||
|       while (container.firstChild) |       while (container.firstChild) { | ||||||
|         container.removeChild(container.firstChild) |         container.removeChild(container.firstChild); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       filters.forEach(function (d) { |       filters.forEach(function (d) { | ||||||
|         var li = document.createElement("li") |         var li = document.createElement("li"); | ||||||
|         var div = document.createElement("div") |         var div = document.createElement("div"); | ||||||
|         container.appendChild(li) |         container.appendChild(li); | ||||||
|         li.appendChild(div) |         li.appendChild(div); | ||||||
|         d.render(div) |         d.render(div); | ||||||
| 
 | 
 | ||||||
|         var button = document.createElement("button") |         var button = document.createElement("button"); | ||||||
|         button.textContent = "" |         button.textContent = ""; | ||||||
|         button.onclick = function () { |         button.onclick = function () { | ||||||
|           distributor.removeFilter(d) |           distributor.removeFilter(d); | ||||||
|         } |         }; | ||||||
|         li.appendChild(button) |         li.appendChild(button); | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       if (container.parentNode === div && filters.length === 0) |       if (container.parentNode === div && filters.length === 0) { | ||||||
|         div.removeChild(container) |         div.removeChild(container); | ||||||
|       else if (filters.length > 0) |       } else if (filters.length > 0) { | ||||||
|         div.appendChild(container) |         div.appendChild(container); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return { render: render, |     return { | ||||||
|  |       render: render, | ||||||
|       filtersChanged: filtersChanged |       filtersChanged: filtersChanged | ||||||
|            } |     }; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,52 +1,58 @@ | ||||||
| define([], function () { | define(["helper"], function (helper) { | ||||||
|   return function (name, key, value, f) { |   "use strict"; | ||||||
|     var negate = false |  | ||||||
|     var refresh |  | ||||||
| 
 | 
 | ||||||
|     var label = document.createElement("label") |   return function (name, key, value, f) { | ||||||
|     var strong = document.createElement("strong") |     var negate = false; | ||||||
|     label.textContent = name + " " |     var refresh; | ||||||
|     label.appendChild(strong) | 
 | ||||||
|  |     var label = document.createElement("label"); | ||||||
|  |     var strong = document.createElement("strong"); | ||||||
|  |     label.textContent = name + " "; | ||||||
|  |     label.appendChild(strong); | ||||||
| 
 | 
 | ||||||
|     function run(d) { |     function run(d) { | ||||||
|       var o = dictGet(d, key.slice(0)) |       var o = helper.dictGet(d, key.slice(0)); | ||||||
| 
 | 
 | ||||||
|       if (f) |       if (f) { | ||||||
|         o = f(o) |         o = f(o); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       return o === value ? !negate : negate |       return o === value ? !negate : negate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function setRefresh(f) { |     function setRefresh(f) { | ||||||
|       refresh = f |       refresh = f; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function draw(el) { |     function draw(el) { | ||||||
|       if (negate) |       if (negate) { | ||||||
|         el.parentNode.classList.add("not") |         el.parentNode.classList.add("not"); | ||||||
|       else |       } else { | ||||||
|         el.parentNode.classList.remove("not") |         el.parentNode.classList.remove("not"); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       strong.textContent = (negate ? "¬" : "" ) + value |       strong.textContent = (negate ? "¬" : "" ) + value; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function render(el) { |     function render(el) { | ||||||
|       el.appendChild(label) |       el.appendChild(label); | ||||||
|       draw(el) |       draw(el); | ||||||
| 
 | 
 | ||||||
|       label.onclick = function () { |       label.onclick = function () { | ||||||
|         negate = !negate |         negate = !negate; | ||||||
| 
 | 
 | ||||||
|         draw(el) |         draw(el); | ||||||
| 
 | 
 | ||||||
|         if (refresh) |         if (refresh) { | ||||||
|           refresh() |           refresh(); | ||||||
|         } |         } | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return { run: run, |     return { | ||||||
|  |       run: run, | ||||||
|       setRefresh: setRefresh, |       setRefresh: setRefresh, | ||||||
|       render: render |       render: render | ||||||
|            } |     }; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,33 +1,38 @@ | ||||||
| define([], function () { | define(function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (filter) { |   return function (filter) { | ||||||
|     return function (data) { |     return function (data) { | ||||||
|       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) { | ||||||
|         var r |         var r; | ||||||
|         if (d.node) |         if (d.node) { | ||||||
|           r = filter(d.node) |           r = filter(d.node); | ||||||
|         else |         } else { | ||||||
|           r = filter({}) |           r = filter({}); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (r) |         if (r) { | ||||||
|           filteredIds.add(d.id) |           filteredIds.add(d.id); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         return r |         return r; | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       n.graph.links = data.graph.links.filter(function (d) { |       n.graph.links = data.graph.links.filter(function (d) { | ||||||
|         return filteredIds.has(d.source.id) && filteredIds.has(d.target.id) |         return filteredIds.has(d.source.id) && filteredIds.has(d.target.id); | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       return n |       return n; | ||||||
|     } |     }; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										162
									
								
								lib/gui.js
									
										
									
									
									
								
							
							
						
						
									
										162
									
								
								lib/gui.js
									
										
									
									
									
								
							|  | @ -5,121 +5,125 @@ define([ "chroma-js", "map", "sidebar", "tabs", "container", "meshstats", | ||||||
|   function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, |   function (chroma, Map, Sidebar, Tabs, Container, Meshstats, Legend, Linklist, | ||||||
|             Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph, |             Nodelist, SimpleNodelist, Infobox, Proportions, ForceGraph, | ||||||
|             Title, About, DataDistributor, FilterGUI) { |             Title, About, DataDistributor, FilterGUI) { | ||||||
|  |     "use strict"; | ||||||
|  | 
 | ||||||
|     return function (config, router) { |     return function (config, router) { | ||||||
|     var self = this |       var self = this; | ||||||
|     var content |       var content; | ||||||
|     var contentDiv |       var contentDiv; | ||||||
| 
 | 
 | ||||||
|     var linkScale = chroma.scale(chroma.interpolate.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]) |       var linkScale = chroma.scale(chroma.bezier(["#04C714", "#FF5500", "#F02311"])).domain([1, 5]); | ||||||
|     var sidebar |       var sidebar; | ||||||
| 
 | 
 | ||||||
|     var buttons = document.createElement("div") |       var buttons = document.createElement("div"); | ||||||
|     buttons.classList.add("buttons") |       buttons.classList.add("buttons"); | ||||||
| 
 | 
 | ||||||
|     var fanout = new DataDistributor() |       var fanout = new DataDistributor(); | ||||||
|     var fanoutUnfiltered = new DataDistributor() |       var fanoutUnfiltered = new DataDistributor(); | ||||||
|     fanoutUnfiltered.add(fanout) |       fanoutUnfiltered.add(fanout); | ||||||
| 
 | 
 | ||||||
|       function removeContent() { |       function removeContent() { | ||||||
|       if (!content) |         if (!content) { | ||||||
|         return |           return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|       router.removeTarget(content) |         router.removeTarget(content); | ||||||
|       fanout.remove(content) |         fanout.remove(content); | ||||||
| 
 | 
 | ||||||
|       content.destroy() |         content.destroy(); | ||||||
| 
 | 
 | ||||||
|       content = null |         content = null; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function addContent(K) { |       function addContent(K) { | ||||||
|       removeContent() |         removeContent(); | ||||||
| 
 | 
 | ||||||
|       content = new K(config, linkScale, sidebar.getWidth, router, buttons) |         content = new K(config, linkScale, sidebar.getWidth, router, buttons); | ||||||
|       content.render(contentDiv) |         content.render(contentDiv); | ||||||
| 
 | 
 | ||||||
|       fanout.add(content) |         fanout.add(content); | ||||||
|       router.addTarget(content) |         router.addTarget(content); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function mkView(K) { |       function mkView(K) { | ||||||
|         return function () { |         return function () { | ||||||
|         addContent(K) |           addContent(K); | ||||||
|       } |         }; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     var loader = document.getElementsByClassName("loader")[0] |       var loader = document.getElementsByClassName("loader")[0]; | ||||||
|     loader.classList.add("hide") |       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); | ||||||
| 
 | 
 | ||||||
|     sidebar = new Sidebar(document.body) |       sidebar = new Sidebar(document.body); | ||||||
| 
 | 
 | ||||||
|     contentDiv.appendChild(buttons) |       contentDiv.appendChild(buttons); | ||||||
| 
 | 
 | ||||||
|     var buttonToggle = document.createElement("button") |       var buttonToggle = document.createElement("button"); | ||||||
|     buttonToggle.textContent = "\uF133" |       buttonToggle.textContent = "\uF133"; | ||||||
|       buttonToggle.onclick = function () { |       buttonToggle.onclick = function () { | ||||||
|       if (content.constructor === Map) |         if (content.constructor === Map) { | ||||||
|         router.view("g") |           router.view("g"); | ||||||
|       else |         } else { | ||||||
|         router.view("m") |           router.view("m"); | ||||||
|         } |         } | ||||||
|  |       }; | ||||||
| 
 | 
 | ||||||
|     buttons.appendChild(buttonToggle) |       buttons.appendChild(buttonToggle); | ||||||
| 
 | 
 | ||||||
|     var title = new Title(config) |       var title = new Title(config); | ||||||
| 
 | 
 | ||||||
|     var header = new Container("header") |       var header = new Container("header"); | ||||||
|     var infobox = new Infobox(config, sidebar, router) |       var infobox = new Infobox(config, sidebar, router); | ||||||
|     var tabs = new Tabs() |       var tabs = new Tabs(); | ||||||
|     var overview = new Container() |       var overview = new Container(); | ||||||
|     var meshstats = new Meshstats(config) |       var meshstats = new Meshstats(config); | ||||||
|     var legend = new Legend() |       var legend = new Legend(); | ||||||
|     var newnodeslist = new SimpleNodelist("new", "firstseen", router, "Neue Knoten") |       var newnodeslist = new SimpleNodelist("new", "firstseen", router, "Neue Knoten"); | ||||||
|     var lostnodeslist = new SimpleNodelist("lost", "lastseen", router, "Verschwundene Knoten") |       var lostnodeslist = new SimpleNodelist("lost", "lastseen", router, "Verschwundene Knoten"); | ||||||
|     var nodelist = new Nodelist(router) |       var nodelist = new Nodelist(router); | ||||||
|     var linklist = new Linklist(linkScale, router) |       var linklist = new Linklist(linkScale, router); | ||||||
|     var statistics = new Proportions(config, fanout) |       var statistics = new Proportions(config, fanout); | ||||||
|     var about = new About() |       var about = new About(); | ||||||
| 
 | 
 | ||||||
|     fanoutUnfiltered.add(meshstats) |       fanoutUnfiltered.add(meshstats); | ||||||
|     fanoutUnfiltered.add(newnodeslist) |       fanoutUnfiltered.add(newnodeslist); | ||||||
|     fanoutUnfiltered.add(lostnodeslist) |       fanoutUnfiltered.add(lostnodeslist); | ||||||
|     fanout.add(nodelist) |       fanout.add(nodelist); | ||||||
|     fanout.add(linklist) |       fanout.add(linklist); | ||||||
|     fanout.add(statistics) |       fanout.add(statistics); | ||||||
| 
 | 
 | ||||||
|     sidebar.add(header) |       sidebar.add(header); | ||||||
|     header.add(meshstats) |       header.add(meshstats); | ||||||
|     header.add(legend) |       header.add(legend); | ||||||
| 
 | 
 | ||||||
|     overview.add(newnodeslist) |       overview.add(newnodeslist); | ||||||
|     overview.add(lostnodeslist) |       overview.add(lostnodeslist); | ||||||
| 
 | 
 | ||||||
|     var filterGUI = new FilterGUI(fanout) |       var filterGUI = new FilterGUI(fanout); | ||||||
|     fanout.watchFilters(filterGUI) |       fanout.watchFilters(filterGUI); | ||||||
|     header.add(filterGUI) |       header.add(filterGUI); | ||||||
| 
 | 
 | ||||||
|     sidebar.add(tabs) |       sidebar.add(tabs); | ||||||
|     tabs.add("Aktuelles", overview) |       tabs.add("Aktuelles", overview); | ||||||
|     tabs.add("Knoten", nodelist) |       tabs.add("Knoten", nodelist); | ||||||
|     tabs.add("Verbindungen", linklist) |       tabs.add("Verbindungen", linklist); | ||||||
|     tabs.add("Statistiken", statistics) |       tabs.add("Statistiken", statistics); | ||||||
|     tabs.add("Über", about) |       tabs.add("Über", about); | ||||||
| 
 | 
 | ||||||
|     router.addTarget(title) |       router.addTarget(title); | ||||||
|     router.addTarget(infobox) |       router.addTarget(infobox); | ||||||
| 
 | 
 | ||||||
|     router.addView("m", mkView(Map)) |       router.addView("m", mkView(Map)); | ||||||
|     router.addView("g", mkView(ForceGraph)) |       router.addView("g", mkView(ForceGraph)); | ||||||
| 
 | 
 | ||||||
|     router.view("m") |       router.view("m"); | ||||||
| 
 | 
 | ||||||
|     self.setData = fanoutUnfiltered.setData |       self.setData = fanoutUnfiltered.setData; | ||||||
| 
 | 
 | ||||||
|     return self |       return self; | ||||||
|   } |     }; | ||||||
| }) |   }); | ||||||
|  |  | ||||||
|  | @ -1,48 +1,52 @@ | ||||||
| define(function () { | define(["helper"], function (helper) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   function showStatImg(o, d) { |   function showStatImg(o, d) { | ||||||
|     var subst = {} |     var subst = {}; | ||||||
|     subst["{SOURCE}"] = d.source.node_id |     subst["{SOURCE}"] = d.source.node_id; | ||||||
| 	subst["{SOURCE_NAME}"] = d.source.node.nodeinfo.hostname ? d.source.node.nodeinfo.hostname : "unknown" |     subst["{SOURCE_NAME}"] = d.source.node.nodeinfo.hostname ? d.source.node.nodeinfo.hostname : "unknown"; | ||||||
|     subst["{TARGET}"] = d.target.node_id |     subst["{TARGET}"] = d.target.node_id; | ||||||
| 	subst["{TARGET_NAME}"] = d.target.node.nodeinfo.hostname ? d.target.node.nodeinfo.hostname : "unknown" |     subst["{TARGET_NAME}"] = d.target.node.nodeinfo.hostname ? d.target.node.nodeinfo.hostname : "unknown"; | ||||||
|     return showStat(o, subst) |     return helper.showStat(o, subst); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return function (config, el, router, d) { |   return function (config, el, router, d) { | ||||||
|     var unknown = !(d.source.node) |     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) { |     if (!unknown) { | ||||||
|       a1.href = "#" |       a1.href = "#"; | ||||||
|       a1.onclick = router.node(d.source.node) |       a1.onclick = router.node(d.source.node); | ||||||
|     } |     } | ||||||
|     a1.textContent = unknown ? d.source.id : 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(" \uF3D6 ")); | ||||||
|     var a2 = document.createElement("a") |     h2.className = "ion-inside"; | ||||||
|     a2.href = "#" |     var a2 = document.createElement("a"); | ||||||
|     a2.onclick = router.node(d.target.node) |     a2.href = "#"; | ||||||
|     a2.textContent = d.target.node.nodeinfo.hostname |     a2.onclick = router.node(d.target.node); | ||||||
|     h2.appendChild(a2) |     a2.textContent = d.target.node.nodeinfo.hostname; | ||||||
|     el.appendChild(h2) |     h2.appendChild(a2); | ||||||
|  |     el.appendChild(h2); | ||||||
| 
 | 
 | ||||||
|     var attributes = document.createElement("table") |     var attributes = document.createElement("table"); | ||||||
|     attributes.classList.add("attributes") |     attributes.classList.add("attributes"); | ||||||
| 
 | 
 | ||||||
|     attributeEntry(attributes, "TQ", showTq(d)) |     helper.attributeEntry(attributes, "TQ", helper.showTq(d)); | ||||||
|     attributeEntry(attributes, "Entfernung", showDistance(d)) |     helper.attributeEntry(attributes, "Entfernung", helper.showDistance(d)); | ||||||
|     attributeEntry(attributes, "Typ", d.type) |     helper.attributeEntry(attributes, "Typ", d.type); | ||||||
|     var hw1 = unknown ? null : dictGet(d.source.node.nodeinfo, ["hardware", "model"]) |     var hw1 = unknown ? null : helper.dictGet(d.source.node.nodeinfo, ["hardware", "model"]); | ||||||
|     var hw2 = dictGet(d.target.node.nodeinfo, ["hardware", "model"]) |     var hw2 = helper.dictGet(d.target.node.nodeinfo, ["hardware", "model"]); | ||||||
|     attributeEntry(attributes, "Hardware", (hw1 != null ? hw1 : "unbekannt") + " – " + (hw2 != null ? hw2 : "unbekannt")) |     helper.attributeEntry(attributes, "Hardware", (hw1 != null ? hw1 : "unbekannt") + " – " + (hw2 != null ? hw2 : "unbekannt")); | ||||||
|     el.appendChild(attributes) |     el.appendChild(attributes); | ||||||
| 
 | 
 | ||||||
|     if (config.linkInfos) |     if (config.linkInfos) { | ||||||
|       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, d)) |         el.appendChild(showStatImg(linkInfo, d)); | ||||||
|       }) |       }); | ||||||
|     } |     } | ||||||
| }) |   }; | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | @ -1,100 +1,105 @@ | ||||||
| define(function () { | define(["helper"], function (helper) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (config, el, router, d) { |   return function (config, el, router, d) { | ||||||
|     var sidebarTitle = document.createElement("h2") |     var sidebarTitle = document.createElement("h2"); | ||||||
|     sidebarTitle.textContent = "Location: " + d.toString() |     sidebarTitle.textContent = "Location: " + d.toString(); | ||||||
|     el.appendChild(sidebarTitle) |     el.appendChild(sidebarTitle); | ||||||
| 
 | 
 | ||||||
|     getJSON("https://nominatim.openstreetmap.org/reverse?format=json&lat=" + d.lat + "&lon=" + d.lng + "&zoom=18&addressdetails=0") |     helper.getJSON("https://nominatim.openstreetmap.org/reverse?format=json&lat=" + d.lat + "&lon=" + d.lng + "&zoom=18&addressdetails=0") | ||||||
|       .then(function (result) { |       .then(function (result) { | ||||||
|       if(result.display_name) |         if (result.display_name) { | ||||||
|         sidebarTitle.textContent = result.display_name |           sidebarTitle.textContent = result.display_name; | ||||||
|     }) |         } | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|     var editLat = document.createElement("input") |     var editLat = document.createElement("input"); | ||||||
|     editLat.type = "text" |     editLat.type = "text"; | ||||||
|     editLat.value = d.lat.toFixed(9) |     editLat.value = d.lat.toFixed(9); | ||||||
|     el.appendChild(createBox("lat", "Breitengrad", editLat)) |     el.appendChild(createBox("lat", "Breitengrad", editLat)); | ||||||
| 
 | 
 | ||||||
|     var editLng = document.createElement("input") |     var editLng = document.createElement("input"); | ||||||
|     editLng.type = "text" |     editLng.type = "text"; | ||||||
|     editLng.value = d.lng.toFixed(9) |     editLng.value = d.lng.toFixed(9); | ||||||
|     el.appendChild(createBox("lng", "Längengrad", editLng)) |     el.appendChild(createBox("lng", "Längengrad", editLng)); | ||||||
| 
 | 
 | ||||||
|     var editUci = document.createElement("textarea") |     var editUci = document.createElement("textarea"); | ||||||
|     editUci.value = |     editUci.value = | ||||||
|       "uci set gluon-node-info.@location[0]='location'; " + |       "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].share_location='1';" + | ||||||
|       "uci set gluon-node-info.@location[0].latitude='" + d.lat.toFixed(9) + "';" + |       "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 set gluon-node-info.@location[0].longitude='" + d.lng.toFixed(9) + "';" + | ||||||
|       "uci commit gluon-node-info" |       "uci commit gluon-node-info"; | ||||||
| 
 | 
 | ||||||
|     el.appendChild(createBox("uci", "Befehl", editUci, false)) |     el.appendChild(createBox("uci", "Befehl", editUci, false)); | ||||||
| 
 | 
 | ||||||
|     var linkPlain = document.createElement("a") |     var linkPlain = document.createElement("a"); | ||||||
|     linkPlain.textContent = "plain" |     linkPlain.textContent = "plain"; | ||||||
|     linkPlain.onclick = function () { |     linkPlain.onclick = function () { | ||||||
|       switch2plain() |       switch2plain(); | ||||||
|       return false |       return false; | ||||||
|     } |     }; | ||||||
|     linkPlain.href = "#" |     linkPlain.href = "#"; | ||||||
| 
 | 
 | ||||||
|     var linkUci = document.createElement("a") |     var linkUci = document.createElement("a"); | ||||||
|     linkUci.textContent = "uci" |     linkUci.textContent = "uci"; | ||||||
|     linkUci.onclick = function () { |     linkUci.onclick = function () { | ||||||
|       switch2uci() |       switch2uci(); | ||||||
|       return false |       return false; | ||||||
|     } |     }; | ||||||
|     linkUci.href = "#" |     linkUci.href = "#"; | ||||||
| 
 | 
 | ||||||
|     var hintText = document.createElement("p") |     var hintText = document.createElement("p"); | ||||||
|     hintText.appendChild(document.createTextNode("Du kannst zwischen ")) |     hintText.appendChild(document.createTextNode("Du kannst zwischen ")); | ||||||
|     hintText.appendChild(linkPlain) |     hintText.appendChild(linkPlain); | ||||||
|     hintText.appendChild(document.createTextNode(" und ")) |     hintText.appendChild(document.createTextNode(" und ")); | ||||||
|     hintText.appendChild(linkUci) |     hintText.appendChild(linkUci); | ||||||
|     hintText.appendChild(document.createTextNode(" wechseln.")) |     hintText.appendChild(document.createTextNode(" wechseln.")); | ||||||
|     el.appendChild(hintText) |     el.appendChild(hintText); | ||||||
| 
 | 
 | ||||||
|     function createBox(name, title, inputElem, isVisible) { |     function createBox(name, title, inputElem, isVisible) { | ||||||
|       var visible = typeof isVisible !== "undefined" ?  isVisible : true |       var visible = typeof isVisible !== "undefined" ? isVisible : true; | ||||||
|       var box = document.createElement("div") |       var box = document.createElement("div"); | ||||||
|       var heading = document.createElement("h3") |       var heading = document.createElement("h3"); | ||||||
|       heading.textContent = title |       heading.textContent = title; | ||||||
|       box.appendChild(heading) |       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 = "Kopieren" |       btn.title = "Kopieren"; | ||||||
|       btn.onclick = function() { copy2clip(inputElem.id) } |       btn.onclick = function () { | ||||||
|       inputElem.id = "location-" + name |         copy2clip(inputElem.id); | ||||||
|       inputElem.readOnly = true |       }; | ||||||
|       var line = document.createElement("p") |       inputElem.id = "location-" + name; | ||||||
|       line.appendChild(inputElem) |       inputElem.readOnly = true; | ||||||
|       line.appendChild(btn) |       var line = document.createElement("p"); | ||||||
|       box.appendChild(line) |       line.appendChild(inputElem); | ||||||
|       box.id = "box-" + name |       line.appendChild(btn); | ||||||
|       box.style.display = visible ? "block" : "none" |       box.appendChild(line); | ||||||
|       return box |       box.id = "box-" + name; | ||||||
|  |       box.style.display = visible ? "block" : "none"; | ||||||
|  |       return box; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function copy2clip(id) { |     function copy2clip(id) { | ||||||
|       var copyField = document.querySelector("#" + id) |       var copyField = document.querySelector("#" + id); | ||||||
|       copyField.select() |       copyField.select(); | ||||||
|       try { |       try { | ||||||
|         document.execCommand("copy") |         document.execCommand("copy"); | ||||||
|       } catch (err) { |       } catch (err) { | ||||||
|         console.log(err) |         console.warn(err); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function switch2plain() { |     function switch2plain() { | ||||||
|       document.getElementById("box-uci").style.display = "none" |       document.getElementById("box-uci").style.display = "none"; | ||||||
|       document.getElementById("box-lat").style.display = "block" |       document.getElementById("box-lat").style.display = "block"; | ||||||
|       document.getElementById("box-lng").style.display = "block" |       document.getElementById("box-lng").style.display = "block"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function switch2uci() { |     function switch2uci() { | ||||||
|       document.getElementById("box-uci").style.display = "block" |       document.getElementById("box-uci").style.display = "block"; | ||||||
|       document.getElementById("box-lat").style.display = "none" |       document.getElementById("box-lat").style.display = "none"; | ||||||
|       document.getElementById("box-lng").style.display = "none" |       document.getElementById("box-lng").style.display = "none"; | ||||||
|     } |     } | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,51 +1,53 @@ | ||||||
| define(["infobox/link", "infobox/node", "infobox/location"], function (Link, Node, Location) { | define(["infobox/link", "infobox/node", "infobox/location"], function (Link, Node, Location) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (config, sidebar, router) { |   return function (config, sidebar, router) { | ||||||
|     var self = this |     var self = this; | ||||||
|     var el |     var el; | ||||||
| 
 | 
 | ||||||
|     function destroy() { |     function destroy() { | ||||||
|       if (el && el.parentNode) { |       if (el && el.parentNode) { | ||||||
|         el.parentNode.removeChild(el) |         el.parentNode.removeChild(el); | ||||||
|         el = undefined |         el = undefined; | ||||||
|         sidebar.reveal() |         sidebar.reveal(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function create() { |     function create() { | ||||||
|       destroy() |       destroy(); | ||||||
|       sidebar.ensureVisible() |       sidebar.ensureVisible(); | ||||||
|       sidebar.hide() |       sidebar.hide(); | ||||||
| 
 | 
 | ||||||
|       el = document.createElement("div") |       el = document.createElement("div"); | ||||||
|       sidebar.container.insertBefore(el, sidebar.container.firstChild) |       sidebar.container.insertBefore(el, sidebar.container.firstChild); | ||||||
| 
 | 
 | ||||||
|       el.scrollIntoView(false) |       el.scrollIntoView(false); | ||||||
|       el.classList.add("infobox") |       el.classList.add("infobox"); | ||||||
|       el.destroy = destroy |       el.destroy = destroy; | ||||||
| 
 | 
 | ||||||
|       var closeButton = document.createElement("button") |       var closeButton = document.createElement("button"); | ||||||
|       closeButton.classList.add("close") |       closeButton.classList.add("close"); | ||||||
|       closeButton.onclick = router.reset |       closeButton.onclick = router.reset; | ||||||
|       el.appendChild(closeButton) |       el.appendChild(closeButton); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     self.resetView = destroy |     self.resetView = destroy; | ||||||
| 
 | 
 | ||||||
|     self.gotoNode = function (d) { |     self.gotoNode = function (d) { | ||||||
|       create() |       create(); | ||||||
|       new Node(config, el, router, d) |       Node(config, el, router, d); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.gotoLink = function (d) { |     self.gotoLink = function (d) { | ||||||
|       create() |       create(); | ||||||
|       new Link(config, el, router, d) |       Link(config, el, router, d); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.gotoLocation = function (d) { |     self.gotoLocation = function (d) { | ||||||
|       create() |       create(); | ||||||
|       new Location(config, el, router, d) |       Location(config, el, router, d); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     return self |     return self; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,84 +1,93 @@ | ||||||
| define(["moment", "numeral", "tablesort", "tablesort.numeric"], | define(["moment", "tablesort", "helper", "moment.de"], | ||||||
|   function (moment, numeral, Tablesort) { |   function (moment, Tablesort, helper) { | ||||||
|  |     "use strict"; | ||||||
|  | 
 | ||||||
|     function showGeoURI(d) { |     function showGeoURI(d) { | ||||||
|       function showLatitude(d) { |       function showLatitude(d) { | ||||||
|       var suffix = Math.sign(d) > -1 ? "' N" : "' S" |         var suffix = Math.sign(d) > -1 ? "' N" : "' S"; | ||||||
|       d = Math.abs(d) |         d = Math.abs(d); | ||||||
|       var a = Math.floor(d) |         var a = Math.floor(d); | ||||||
|       var min = (d * 60) % 60 |         var min = (d * 60) % 60; | ||||||
|       a = (a < 10 ? "0" : "") + a |         a = (a < 10 ? "0" : "") + a; | ||||||
| 
 | 
 | ||||||
|       return a + "° " + numeral(min).format("0.000") + suffix |         return a + "° " + min.toFixed(3) + suffix; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function showLongitude(d) { |       function showLongitude(d) { | ||||||
|       var suffix = Math.sign(d) > -1 ? "' E" : "' W" |         var suffix = Math.sign(d) > -1 ? "' E" : "' W"; | ||||||
|       d = Math.abs(d) |         d = Math.abs(d); | ||||||
|       var a = Math.floor(d) |         var a = Math.floor(d); | ||||||
|       var min = (d * 60) % 60 |         var min = (d * 60) % 60; | ||||||
|       a = (a < 100 ? "0" + (a < 10 ? "0" : "") : "") + a |         a = (a < 100 ? "0" + (a < 10 ? "0" : "") : "") + a; | ||||||
| 
 | 
 | ||||||
|       return a + "° " + numeral(min).format("0.000") + suffix |         return a + "° " + min.toFixed(3) + suffix; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     if (!has_location(d)) |       if (!helper.hasLocation(d)) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       return function (el) { |       return function (el) { | ||||||
|       var latitude = d.nodeinfo.location.latitude |         var latitude = d.nodeinfo.location.latitude; | ||||||
|       var longitude = d.nodeinfo.location.longitude |         var longitude = d.nodeinfo.location.longitude; | ||||||
|       var a = document.createElement("a") |         var a = document.createElement("a"); | ||||||
|         a.textContent = showLatitude(latitude) + " " + |         a.textContent = showLatitude(latitude) + " " + | ||||||
|                       showLongitude(longitude) |           showLongitude(longitude); | ||||||
| 
 | 
 | ||||||
|       a.href = "geo:" + latitude + "," + longitude |         a.href = "geo:" + latitude + "," + longitude; | ||||||
|       el.appendChild(a) |         el.appendChild(a); | ||||||
|     } |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showStatus(d) { |     function showStatus(d) { | ||||||
|       return function (el) { |       return function (el) { | ||||||
|       el.classList.add(d.flags.unseen ? "unseen" : (d.flags.online ? "online" : "offline")) |         el.classList.add(d.flags.unseen ? "unseen" : (d.flags.online ? "online" : "offline")); | ||||||
|       if (d.flags.online) |         if (d.flags.online) { | ||||||
|         el.textContent = "online, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY,  H:mm:ss") + ")" |           el.textContent = "online, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY,  H:mm:ss") + ")"; | ||||||
|       else |         } else { | ||||||
|         el.textContent = "offline, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY,  H:mm:ss") + ")" |           el.textContent = "offline, letzte Nachricht " + d.lastseen.fromNow() + " (" + d.lastseen.format("DD.MM.YYYY,  H:mm:ss") + ")"; | ||||||
|         } |         } | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showFirmware(d) { |     function showFirmware(d) { | ||||||
|     var release = dictGet(d.nodeinfo, ["software", "firmware", "release"]) |       var release = helper.dictGet(d.nodeinfo, ["software", "firmware", "release"]); | ||||||
|     var base = dictGet(d.nodeinfo, ["software", "firmware", "base"]) |       var base = helper.dictGet(d.nodeinfo, ["software", "firmware", "base"]); | ||||||
| 
 | 
 | ||||||
|     if (release === null || base === null) |       if (release === null || base === null) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     return release + " / " + base |       return release + " / " + base; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showSite(d, config) { |     function showSite(d, config) { | ||||||
|     var site = dictGet(d.nodeinfo, ["system", "site_code"]) |       var site = helper.dictGet(d.nodeinfo, ["system", "site_code"]); | ||||||
|     var rt = site |       var rt = site; | ||||||
|     if (config.siteNames) |       if (config.siteNames) { | ||||||
|         config.siteNames.forEach(function (t) { |         config.siteNames.forEach(function (t) { | ||||||
|         if(site === t.site) |           if (site === t.site) { | ||||||
|           rt = t.name |             rt = t.name; | ||||||
|       }) |           } | ||||||
|     return rt |         }); | ||||||
|  |       } | ||||||
|  |       return rt; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showUptime(d) { |     function showUptime(d) { | ||||||
|     if (!("uptime" in d.statistics)) |       if (!("uptime" in d.statistics)) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     return moment.duration(d.statistics.uptime, "seconds").humanize() |       return moment.duration(d.statistics.uptime, "seconds").humanize(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showFirstseen(d) { |     function showFirstseen(d) { | ||||||
|     if (!("firstseen" in d)) |       if (!("firstseen" in d)) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     return d.firstseen.fromNow(true) |       return d.firstseen.fromNow(true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function wifiChannelAlias(ch) { |     function wifiChannelAlias(ch) { | ||||||
|  | @ -115,467 +124,511 @@ define(["moment", "numeral", "tablesort", "tablesort.numeric"], | ||||||
|         "132": "5660 MHz (DFS) !!", |         "132": "5660 MHz (DFS) !!", | ||||||
|         "136": "5680 MHz (DFS) !!", |         "136": "5680 MHz (DFS) !!", | ||||||
|         "140": "5700 MHz (DFS) !!" |         "140": "5700 MHz (DFS) !!" | ||||||
|  |       }; | ||||||
|  |       if (!(ch in chlist)) { | ||||||
|  |         return ""; | ||||||
|  |       } else { | ||||||
|  |         return chlist[ch]; | ||||||
|       } |       } | ||||||
|     if (!(ch in chlist)) |  | ||||||
|       return "" |  | ||||||
|     else |  | ||||||
|       return chlist[ch] |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showWifiChannel(ch) { |     function showWifiChannel(ch) { | ||||||
|     if (!ch) |       if (!ch) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     return ch + " (" + wifiChannelAlias(ch) + ")" |       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) { |  | ||||||
|       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")) |  | ||||||
| 
 |  | ||||||
|       var span = document.createElement("span") |  | ||||||
|       span.classList.add("clients") |  | ||||||
|       span.textContent = " ".repeat(d.statistics.clients) |  | ||||||
|       el.appendChild(span) |  | ||||||
| 
 |  | ||||||
|       var spanmesh = document.createElement("span") |  | ||||||
|       spanmesh.classList.add("clientsMesh") |  | ||||||
|       spanmesh.textContent = " ".repeat(meshclients - d.statistics.clients) |  | ||||||
|       el.appendChild(spanmesh) |  | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       var meshclients = getMeshClients(d); | ||||||
|  |       resetMeshClients(d); | ||||||
|  |       var before = "     ("; | ||||||
|  |       var after = " in der lokalen Wolke)"; | ||||||
|  |       return function (el) { | ||||||
|  |         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")); | ||||||
|  | 
 | ||||||
|  |         var span = document.createElement("span"); | ||||||
|  |         span.classList.add("clients"); | ||||||
|  |         span.textContent = "\uF47E ".repeat(d.statistics.clients); | ||||||
|  |         el.appendChild(span); | ||||||
|  | 
 | ||||||
|  |         var spanmesh = document.createElement("span"); | ||||||
|  |         spanmesh.classList.add("clientsMesh"); | ||||||
|  |         spanmesh.textContent = "\uF47E ".repeat(meshclients - d.statistics.clients); | ||||||
|  |         el.appendChild(spanmesh); | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function getMeshClients(node) { |     function getMeshClients(node) { | ||||||
|     var meshclients = 0 |       var meshclients = 0; | ||||||
|     if (node.statistics && !isNaN(node.statistics.clients)) |       if (node.statistics && !isNaN(node.statistics.clients)) { | ||||||
|       meshclients = node.statistics.clients |         meshclients = node.statistics.clients; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     if (!node) |       if (!node) { | ||||||
|       return 0 |         return 0; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     if (node.parsed) |       if (node.parsed) { | ||||||
|       return 0 |         return 0; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     node.parsed = 1 |       node.parsed = 1; | ||||||
|       node.neighbours.forEach(function (neighbour) { |       node.neighbours.forEach(function (neighbour) { | ||||||
|       if (!neighbour.link.isVPN && neighbour.node) |         if (!neighbour.link.isVPN && neighbour.node) { | ||||||
|         meshclients += getMeshClients(neighbour.node) |           meshclients += getMeshClients(neighbour.node); | ||||||
|     }) |         } | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|     return meshclients |       return meshclients; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function resetMeshClients(node) { |     function resetMeshClients(node) { | ||||||
|     if (!node.parsed) |       if (!node.parsed) { | ||||||
|       return |         return; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     node.parsed = 0 |       node.parsed = 0; | ||||||
| 
 | 
 | ||||||
|       node.neighbours.forEach(function (neighbour) { |       node.neighbours.forEach(function (neighbour) { | ||||||
|       if (!neighbour.link.isVPN && neighbour.node) |         if (!neighbour.link.isVPN && neighbour.node) { | ||||||
|         resetMeshClients(neighbour.node) |           resetMeshClients(neighbour.node); | ||||||
|     }) |         } | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     return |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showMeshClients(d) { |     function showMeshClients(d) { | ||||||
|     if (!d.flags.online) |       if (!d.flags.online) { | ||||||
|       return undefined |         return undefined; | ||||||
| 
 |  | ||||||
|     var meshclients = getMeshClients(d) |  | ||||||
|     resetMeshClients(d) |  | ||||||
|     return function (el) { |  | ||||||
|       el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine")) |  | ||||||
|       el.appendChild(document.createElement("br")) |  | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       var meshclients = getMeshClients(d); | ||||||
|  |       resetMeshClients(d); | ||||||
|  |       return function (el) { | ||||||
|  |         el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : "keine")); | ||||||
|  |         el.appendChild(document.createElement("br")); | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showIPs(d) { |     function showIPs(d) { | ||||||
|     var ips = dictGet(d.nodeinfo, ["network", "addresses"]) |       var ips = helper.dictGet(d.nodeinfo, ["network", "addresses"]); | ||||||
|     if (ips === null) |       if (ips === null) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     ips.sort() |       ips.sort(); | ||||||
| 
 | 
 | ||||||
|       return function (el) { |       return function (el) { | ||||||
|         ips.forEach(function (ip, i) { |         ips.forEach(function (ip, i) { | ||||||
|         var link = !ip.startsWith("fe80:") |           var link = !ip.startsWith("fe80:"); | ||||||
| 
 | 
 | ||||||
|         if (i > 0) |           if (i > 0) { | ||||||
|           el.appendChild(document.createElement("br")) |             el.appendChild(document.createElement("br")); | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|           if (link) { |           if (link) { | ||||||
|           var a = document.createElement("a") |             var a = document.createElement("a"); | ||||||
|           if (ip.includes(".")) |             if (ip.includes(".")) { | ||||||
|             a.href = "http://" + ip + "/" |               a.href = "http://" + ip + "/"; | ||||||
|           else |             } else { | ||||||
|             a.href = "http://[" + ip + "]/" |               a.href = "http://[" + ip + "]/"; | ||||||
|           a.textContent = ip |  | ||||||
|           el.appendChild(a) |  | ||||||
|         } else |  | ||||||
|           el.appendChild(document.createTextNode(ip)) |  | ||||||
|       }) |  | ||||||
|             } |             } | ||||||
|  |             a.textContent = ip; | ||||||
|  |             el.appendChild(a); | ||||||
|  |           } else { | ||||||
|  |             el.appendChild(document.createTextNode(ip)); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showBar(className, v) { |     function showBar(className, v) { | ||||||
|     var span = document.createElement("span") |       var span = document.createElement("span"); | ||||||
|     span.classList.add("bar") |       span.classList.add("bar"); | ||||||
|     span.classList.add(className) |       span.classList.add(className); | ||||||
| 
 | 
 | ||||||
|     var bar = document.createElement("span") |       var bar = document.createElement("span"); | ||||||
|     bar.style.width = (v * 100) + "%" |       bar.style.width = (v * 100) + "%"; | ||||||
|     span.appendChild(bar) |       span.appendChild(bar); | ||||||
| 
 | 
 | ||||||
|     var label = document.createElement("label") |       var label = document.createElement("label"); | ||||||
|     label.textContent = (Math.round(v * 100)) + " %" |       label.textContent = (Math.round(v * 100)) + " %"; | ||||||
|     span.appendChild(label) |       span.appendChild(label); | ||||||
| 
 | 
 | ||||||
|     return span |       return span; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showLoadBar(className, v) { |     function showLoadBar(className, v) { | ||||||
|     var span = document.createElement("span") |       var span = document.createElement("span"); | ||||||
|     span.classList.add("bar") |       span.classList.add("bar"); | ||||||
|     span.classList.add(className) |       span.classList.add(className); | ||||||
| 
 | 
 | ||||||
|     var bar = document.createElement("span") |       var bar = document.createElement("span"); | ||||||
|       if (v >= 1) { |       if (v >= 1) { | ||||||
|     bar.style.width = ((v * 100) % 100) + "%" |         bar.style.width = ((v * 100) % 100) + "%"; | ||||||
|     bar.style.background = "rgba(255, 50, 50, 0.9)" |         bar.style.background = "rgba(255, 50, 50, 0.9)"; | ||||||
|     span.style.background = "rgba(255, 50, 50, 0.6)" |         span.style.background = "rgba(255, 50, 50, 0.6)"; | ||||||
|     span.appendChild(bar) |         span.appendChild(bar); | ||||||
|       } |       } | ||||||
|     else |       else { | ||||||
|     { |         bar.style.width = (v * 100) + "%"; | ||||||
|       bar.style.width = (v * 100) + "%" |         span.appendChild(bar); | ||||||
|       span.appendChild(bar) |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     var label = document.createElement("label") |       var label = document.createElement("label"); | ||||||
|     label.textContent = +(Math.round(v + "e+2")  + "e-2") |       label.textContent = +(Math.round(v + "e+2") + "e-2"); | ||||||
|     span.appendChild(label) |       span.appendChild(label); | ||||||
| 
 | 
 | ||||||
|     return span |       return span; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showLoad(d) { |     function showLoad(d) { | ||||||
|     if (!("loadavg" in d.statistics)) |       if (!("loadavg" in d.statistics)) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       return function (el) { |       return function (el) { | ||||||
|       el.appendChild(showLoadBar("load-avg", d.statistics.loadavg)) |         el.appendChild(showLoadBar("load-avg", d.statistics.loadavg.toFixed(2))); | ||||||
|     } |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showRAM(d) { |     function showRAM(d) { | ||||||
|     if (!("memory_usage" in d.statistics)) |       if (!("memory_usage" in d.statistics)) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       return function (el) { |       return function (el) { | ||||||
|       el.appendChild(showBar("memory-usage", d.statistics.memory_usage)) |         el.appendChild(showBar("memory-usage", d.statistics.memory_usage)); | ||||||
|     } |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showAirtime(band, val) { |     function showAirtime(band, val) { | ||||||
|     if (!val) |       if (!val) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       return function (el) { |       return function (el) { | ||||||
|       el.appendChild(showBar("airtime" + band.toString(), val)) |         el.appendChild(showBar("airtime" + band.toString(), val)); | ||||||
|     } |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function createLink(target, router) { |     function createLink(target, router) { | ||||||
|     if (!target) return document.createTextNode("unknown") |       if (!target) { | ||||||
|     var unknown = !(target.node) |         return document.createTextNode("unknown"); | ||||||
|     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) |       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) { |     function showGateway(d, router) { | ||||||
|     var nh |       var nh; | ||||||
|     if (dictGet(d.statistics, ["nexthop"])) |       if (helper.dictGet(d.statistics, ["nexthop"])) { | ||||||
|       nh = dictGet(d.statistics, ["nexthop"]) |         nh = helper.dictGet(d.statistics, ["nexthop"]); | ||||||
|     if (dictGet(d.statistics, ["gateway_nexthop"])) |       } | ||||||
|       nh = dictGet(d.statistics, ["gateway_nexthop"]) |       if (helper.dictGet(d.statistics, ["gateway_nexthop"])) { | ||||||
|     var gw = dictGet(d.statistics, ["gateway"]) |         nh = helper.dictGet(d.statistics, ["gateway_nexthop"]); | ||||||
|  |       } | ||||||
|  |       var gw = helper.dictGet(d.statistics, ["gateway"]); | ||||||
| 
 | 
 | ||||||
|     if (!gw) return null |       if (!gw) { | ||||||
|  |         return null; | ||||||
|  |       } | ||||||
|       return function (el) { |       return function (el) { | ||||||
|       var num = 0 |         var num = 0; | ||||||
|         while (gw && nh && gw.id !== nh.id && num < 10) { |         while (gw && nh && gw.id !== nh.id && num < 10) { | ||||||
|         if (num !== 0) el.appendChild(document.createTextNode(" -> ")) |           if (num !== 0) { | ||||||
|         el.appendChild(createLink(nh, router)) |             el.appendChild(document.createTextNode(" -> ")); | ||||||
|         num++ |           } | ||||||
|         if (!nh.node || !nh.node.statistics) break |           el.appendChild(createLink(nh, router)); | ||||||
|         if (!dictGet(nh.node.statistics, ["gateway"]) || !dictGet(nh.node.statistics, ["gateway"]).id) break |           num++; | ||||||
|         if (dictGet(nh.node.statistics, ["gateway"]).id !== gw.id) break |           if (!nh.node || !nh.node.statistics) { | ||||||
|         if (dictGet(nh.node.statistics, ["gateway_nexthop"])) |             break; | ||||||
|           nh = dictGet(nh.node.statistics, ["gateway_nexthop"]) |           } | ||||||
|         else if (dictGet(nh.node.statistics, ["nexthop"])) |           if (!helper.dictGet(nh.node.statistics, ["gateway"]) || !helper.dictGet(nh.node.statistics, ["gateway"]).id) { | ||||||
|           nh = dictGet(nh.node.statistics, ["nexthop"]) |             break; | ||||||
|         else |           } | ||||||
|           break |           if (helper.dictGet(nh.node.statistics, ["gateway"]).id !== gw.id) { | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |           if (helper.dictGet(nh.node.statistics, ["gateway_nexthop"])) { | ||||||
|  |             nh = helper.dictGet(nh.node.statistics, ["gateway_nexthop"]); | ||||||
|  |           } else if (helper.dictGet(nh.node.statistics, ["nexthop"])) { | ||||||
|  |             nh = helper.dictGet(nh.node.statistics, ["nexthop"]); | ||||||
|  |           } else { | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|         if (gw && nh && gw.id !== nh.id) { |         if (gw && nh && gw.id !== nh.id) { | ||||||
|         if (num !== 0) el.appendChild(document.createTextNode(" -> ")) |           if (num !== 0) { | ||||||
|         num++ |             el.appendChild(document.createTextNode(" -> ")); | ||||||
|         el.appendChild(document.createTextNode("...")) |  | ||||||
|           } |           } | ||||||
|       if (num !== 0) el.appendChild(document.createTextNode(" -> ")) |           num++; | ||||||
|       el.appendChild(createLink(gw, router)) |           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 = helper.dictGet(d.nodeinfo, ["pages"]); | ||||||
|     if (webpages === null) |       if (webpages === null) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     webpages.sort() |       webpages.sort(); | ||||||
| 
 | 
 | ||||||
|       return function (el) { |       return function (el) { | ||||||
|         webpages.forEach(function (webpage, i) { |         webpages.forEach(function (webpage, i) { | ||||||
|         if (i > 0) |           if (i > 0) { | ||||||
|           el.appendChild(document.createElement("br")) |             el.appendChild(document.createElement("br")); | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         var a = document.createElement("span") |           var a = document.createElement("span"); | ||||||
|         var link = document.createElement("a") |           var link = document.createElement("a"); | ||||||
|         link.href = webpage |           link.href = webpage; | ||||||
|           if (webpage.search(/^https:\/\//i) !== -1) { |           if (webpage.search(/^https:\/\//i) !== -1) { | ||||||
|           var lock = document.createElement("span") |             var lock = document.createElement("span"); | ||||||
|           lock.className = "ion-android-lock" |             lock.className = "ion-android-lock"; | ||||||
|           a.appendChild(lock) |             a.appendChild(lock); | ||||||
|           var t1 = document.createTextNode(" ") |             var t1 = document.createTextNode(" "); | ||||||
|           a.appendChild(t1) |             a.appendChild(t1); | ||||||
|           link.textContent = webpage.replace(/^https:\/\//i, "") |             link.textContent = webpage.replace(/^https:\/\//i, ""); | ||||||
|           } |           } | ||||||
|         else |           else { | ||||||
|           link.textContent = webpage.replace(/^http:\/\//i, "") |             link.textContent = webpage.replace(/^http:\/\//i, ""); | ||||||
|         a.appendChild(link) |  | ||||||
|         el.appendChild(a) |  | ||||||
|       }) |  | ||||||
|           } |           } | ||||||
|  |           a.appendChild(link); | ||||||
|  |           el.appendChild(a); | ||||||
|  |         }); | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showAutoupdate(d) { |     function showAutoupdate(d) { | ||||||
|     var au = dictGet(d.nodeinfo, ["software", "autoupdater"]) |       var au = helper.dictGet(d.nodeinfo, ["software", "autoupdater"]); | ||||||
|     if (!au) |       if (!au) { | ||||||
|       return undefined |         return undefined; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     return au.enabled ? "aktiviert (" + au.branch + ")" : "deaktiviert" |       return au.enabled ? "aktiviert (" + au.branch + ")" : "deaktiviert"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showNodeImg(o, model) { |     function showNodeImg(o, model) { | ||||||
|     if (!model) |       if (!model) { | ||||||
|       return document.createTextNode("Knotenname") |         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) |  | ||||||
| 
 |  | ||||||
|     content = document.createElement("img") |  | ||||||
|     content.id = "routerpicture" |  | ||||||
|     content.classList.add("nodeImg") |  | ||||||
|     content.src = o.thumbnail.replace("{MODELHASH}", modelhash) |  | ||||||
|     content.onerror = function() { |  | ||||||
|       document.getElementById("routerpicdiv").outerHTML = "Knotenname" |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       var content, caption; | ||||||
|  |       var modelhash = model.split("").reduce(function (a, b) { | ||||||
|  |         a = ((a << 5) - a) + b.charCodeAt(0); | ||||||
|  |         return a & a; | ||||||
|  |       }, 0); | ||||||
|  | 
 | ||||||
|  |       content = document.createElement("img"); | ||||||
|  |       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("{MODELHASH}", modelhash) |         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"); | ||||||
|     p.appendChild(content) |       p.appendChild(content); | ||||||
| 
 | 
 | ||||||
|     return content |       return content; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function showStatImg(o, d) { |     function showStatImg(o, d) { | ||||||
|     var subst = {} |       var subst = {}; | ||||||
|     subst["{NODE_ID}"] = d.nodeinfo.node_id ? d.nodeinfo.node_id : "unknown" |       subst["{NODE_ID}"] = d.nodeinfo.node_id ? d.nodeinfo.node_id : "unknown"; | ||||||
|     subst["{NODE_NAME}"] = d.nodeinfo.hostname ? d.nodeinfo.hostname : "unknown" |       subst["{NODE_NAME}"] = d.nodeinfo.hostname ? d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, "_") : "unknown"; | ||||||
|     return showStat(o, subst) |       return helper.showStat(o, subst); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return function (config, el, router, d) { |     return function (config, el, router, d) { | ||||||
|     var attributes = document.createElement("table") |       var attributes = document.createElement("table"); | ||||||
|     attributes.classList.add("attributes") |       attributes.classList.add("attributes"); | ||||||
| 
 | 
 | ||||||
|       if (config.hwImg) { |       if (config.hwImg) { | ||||||
|       var top = document.createElement("div") |         var top = document.createElement("div"); | ||||||
|       top.id = "routerpicdiv" |         top.id = "routerpicdiv"; | ||||||
|         try { |         try { | ||||||
|           config.hwImg.forEach(function (hwImg) { |           config.hwImg.forEach(function (hwImg) { | ||||||
|             try { |             try { | ||||||
|             top.appendChild(showNodeImg(hwImg, dictGet(d, ["nodeinfo", "hardware", "model"]))) |               top.appendChild(showNodeImg(hwImg, helper.dictGet(d, ["nodeinfo", "hardware", "model"]))); | ||||||
|             } catch (err) { |             } catch (err) { | ||||||
|             console.log(err.message) |               console.warn(err.message); | ||||||
|             } |             } | ||||||
|         }) |           }); | ||||||
|         } catch (err) { |         } catch (err) { | ||||||
|         console.log(err.message) |           console.warn(err.message); | ||||||
|         } |         } | ||||||
|       attributeEntry(attributes, top, d.nodeinfo.hostname) |         helper.attributeEntry(attributes, top, d.nodeinfo.hostname); | ||||||
|       } else { |       } 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); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     attributeEntry(attributes, "Status", showStatus(d)) |       helper.attributeEntry(attributes, "Status", showStatus(d)); | ||||||
|     attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null) |       helper.attributeEntry(attributes, "Gateway", d.flags.gateway ? "ja" : null); | ||||||
|     attributeEntry(attributes, "Koordinaten", showGeoURI(d)) |       helper.attributeEntry(attributes, "Koordinaten", showGeoURI(d)); | ||||||
| 
 | 
 | ||||||
|     if (config.showContact) |       if (config.showContact) { | ||||||
|       attributeEntry(attributes, "Kontakt", dictGet(d.nodeinfo, ["owner", "contact"])) |         helper.attributeEntry(attributes, "Kontakt", helper.dictGet(d.nodeinfo, ["owner", "contact"])); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|     attributeEntry(attributes, "Hardware",  dictGet(d.nodeinfo, ["hardware", "model"])) |       helper.attributeEntry(attributes, "Hardware", helper.dictGet(d.nodeinfo, ["hardware", "model"])); | ||||||
|     attributeEntry(attributes, "Primäre MAC", dictGet(d.nodeinfo, ["network", "mac"])) |       helper.attributeEntry(attributes, "Primäre MAC", helper.dictGet(d.nodeinfo, ["network", "mac"])); | ||||||
|     attributeEntry(attributes, "Node ID", dictGet(d.nodeinfo, ["node_id"])) |       helper.attributeEntry(attributes, "Node ID", helper.dictGet(d.nodeinfo, ["node_id"])); | ||||||
|     attributeEntry(attributes, "Firmware", showFirmware(d)) |       helper.attributeEntry(attributes, "Firmware", showFirmware(d)); | ||||||
|     attributeEntry(attributes, "Site", showSite(d, config)) |       helper.attributeEntry(attributes, "Site", showSite(d, config)); | ||||||
|     attributeEntry(attributes, "Uptime", showUptime(d)) |       helper.attributeEntry(attributes, "Uptime", showUptime(d)); | ||||||
|     attributeEntry(attributes, "Teil des Netzes", showFirstseen(d)) |       helper.attributeEntry(attributes, "Teil des Netzes", showFirstseen(d)); | ||||||
|     attributeEntry(attributes, "Kanal 2.4 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan2"]))) |       helper.attributeEntry(attributes, "Kanal 2.4 GHz", showWifiChannel(helper.dictGet(d.nodeinfo, ["wireless", "chan2"]))); | ||||||
|     attributeEntry(attributes, "Kanal 5 GHz", showWifiChannel(dictGet(d.nodeinfo, ["wireless", "chan5"]))) |       helper.attributeEntry(attributes, "Kanal 5 GHz", showWifiChannel(helper.dictGet(d.nodeinfo, ["wireless", "chan5"]))); | ||||||
|     attributeEntry(attributes, "Airtime 2.4 GHz", showAirtime(2, dictGet(d.statistics, ["wireless", "airtime2"]))) |       helper.attributeEntry(attributes, "Airtime 2.4 GHz", showAirtime(2, helper.dictGet(d.statistics, ["wireless", "airtime2"]))); | ||||||
|     attributeEntry(attributes, "Airtime 5 GHz", showAirtime(5, dictGet(d.statistics, ["wireless", "airtime5"]))) |       helper.attributeEntry(attributes, "Airtime 5 GHz", showAirtime(5, helper.dictGet(d.statistics, ["wireless", "airtime5"]))); | ||||||
|     attributeEntry(attributes, "Systemlast", showLoad(d)) |       helper.attributeEntry(attributes, "Systemlast", showLoad(d)); | ||||||
|     attributeEntry(attributes, "Arbeitsspeicher", showRAM(d)) |       helper.attributeEntry(attributes, "Arbeitsspeicher", showRAM(d)); | ||||||
|     attributeEntry(attributes, "IP Adressen", showIPs(d)) |       helper.attributeEntry(attributes, "IP Adressen", showIPs(d)); | ||||||
|     attributeEntry(attributes, "Webseite", showPages(d)) |       helper.attributeEntry(attributes, "Webseite", showPages(d)); | ||||||
|     attributeEntry(attributes, "Gewähltes Gateway", showGateway(d, router)) |       helper.attributeEntry(attributes, "Gewähltes Gateway", showGateway(d, router)); | ||||||
|     attributeEntry(attributes, "Autom. Updates", showAutoupdate(d)) |       helper.attributeEntry(attributes, "Autom. Updates", showAutoupdate(d)); | ||||||
|     attributeEntry(attributes, "Clients", showClients(d), showMeshClients(d)) |       helper.attributeEntry(attributes, "Clients", showClients(d), showMeshClients(d)); | ||||||
| 
 | 
 | ||||||
|     el.appendChild(attributes) |       el.appendChild(attributes); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     if (config.nodeInfos) |       if (config.nodeInfos) { | ||||||
|         config.nodeInfos.forEach(function (nodeInfo) { |         config.nodeInfos.forEach(function (nodeInfo) { | ||||||
|         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)) |           el.appendChild(showStatImg(nodeInfo, d)); | ||||||
|       }) |         }); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       if (d.neighbours.length > 0) { |       if (d.neighbours.length > 0) { | ||||||
|       var h3 = document.createElement("h3") |         var h3 = document.createElement("h3"); | ||||||
|       h3.textContent = "Links (" + d.neighbours.length + ")" |         h3.textContent = "Links (" + d.neighbours.length + ")"; | ||||||
|       el.appendChild(h3) |         el.appendChild(h3); | ||||||
| 
 | 
 | ||||||
|       var table = document.createElement("table") |         var table = document.createElement("table"); | ||||||
|       var thead = document.createElement("thead") |         var thead = document.createElement("thead"); | ||||||
| 
 | 
 | ||||||
|       var tr = document.createElement("tr") |         var tr = document.createElement("tr"); | ||||||
|       var th1 = document.createElement("th") |         var th1 = document.createElement("th"); | ||||||
|       th1.textContent = " " |         th1.textContent = " "; | ||||||
|       tr.appendChild(th1) |         tr.appendChild(th1); | ||||||
| 
 | 
 | ||||||
|       var th2 = document.createElement("th") |         var th2 = document.createElement("th"); | ||||||
|       th2.textContent = "Knoten" |         th2.textContent = "Knoten"; | ||||||
|       th2.classList.add("sort-default") |         th2.classList.add("sort-default"); | ||||||
|       tr.appendChild(th2) |         tr.appendChild(th2); | ||||||
| 
 | 
 | ||||||
|       var th3 = document.createElement("th") |         var th3 = document.createElement("th"); | ||||||
|       th3.textContent = "TQ" |         th3.textContent = "TQ"; | ||||||
|       tr.appendChild(th3) |         tr.appendChild(th3); | ||||||
| 
 | 
 | ||||||
|       var th4 = document.createElement("th") |         var th4 = document.createElement("th"); | ||||||
|       th4.textContent = "Typ" |         th4.textContent = "Typ"; | ||||||
|       tr.appendChild(th4) |         tr.appendChild(th4); | ||||||
| 
 | 
 | ||||||
|       var th5 = document.createElement("th") |         var th5 = document.createElement("th"); | ||||||
|       th5.textContent = "Entfernung" |         th5.textContent = "Entfernung"; | ||||||
|       tr.appendChild(th5) |         tr.appendChild(th5); | ||||||
| 
 | 
 | ||||||
|       thead.appendChild(tr) |         thead.appendChild(tr); | ||||||
|       table.appendChild(thead) |         table.appendChild(thead); | ||||||
| 
 | 
 | ||||||
|       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 unknown = !(d.node); | ||||||
|         var tr = document.createElement("tr") |           var tr = document.createElement("tr"); | ||||||
| 
 | 
 | ||||||
|         var td1 = document.createElement("td") |           var td1 = document.createElement("td"); | ||||||
|         td1.appendChild(document.createTextNode(d.incoming ? " ← " : " → ")) |           td1.className = "ion-inside"; | ||||||
|         tr.appendChild(td1) |           td1.appendChild(document.createTextNode(d.incoming ? " \uF3D5 " : " \uF3D6 ")); | ||||||
|  |           tr.appendChild(td1); | ||||||
| 
 | 
 | ||||||
|         var td2 = document.createElement("td") |           var td2 = document.createElement("td"); | ||||||
|         td2.appendChild(createLink(d, router)) |           td2.appendChild(createLink(d, router)); | ||||||
| 
 | 
 | ||||||
|         if (!unknown && has_location(d.node)) { |           if (!unknown && helper.hasLocation(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"); | ||||||
|           td2.appendChild(span) |             td2.appendChild(span); | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|         tr.appendChild(td2) |           tr.appendChild(td2); | ||||||
| 
 | 
 | ||||||
|         var td3 = document.createElement("td") |           var td3 = document.createElement("td"); | ||||||
|         var a2 = document.createElement("a") |           var a2 = document.createElement("a"); | ||||||
|         a2.href = "#" |           a2.href = "#"; | ||||||
|         a2.textContent = showTq(d.link) |           a2.textContent = helper.showTq(d.link); | ||||||
|         a2.onclick = router.link(d.link) |           a2.onclick = router.link(d.link); | ||||||
|         td3.appendChild(a2) |           td3.appendChild(a2); | ||||||
|         tr.appendChild(td3) |           tr.appendChild(td3); | ||||||
| 
 | 
 | ||||||
|         var td4 = document.createElement("td") |           var td4 = document.createElement("td"); | ||||||
|         var a3 = document.createElement("a") |           var a3 = document.createElement("a"); | ||||||
|         a3.href = "#" |           a3.href = "#"; | ||||||
|         a3.textContent = d.link.type |           a3.textContent = d.link.type; | ||||||
|         a3.onclick = router.link(d.link) |           a3.onclick = router.link(d.link); | ||||||
|         td4.appendChild(a3) |           td4.appendChild(a3); | ||||||
|         tr.appendChild(td4) |           tr.appendChild(td4); | ||||||
| 
 | 
 | ||||||
|         var td5 = document.createElement("td") |           var td5 = document.createElement("td"); | ||||||
|         var a4 = document.createElement("a") |           var a4 = document.createElement("a"); | ||||||
|         a4.href = "#" |           a4.href = "#"; | ||||||
|         a4.textContent = showDistance(d.link) |           a4.textContent = helper.showDistance(d.link); | ||||||
|         a4.onclick = router.link(d.link) |           a4.onclick = router.link(d.link); | ||||||
|         td5.appendChild(a4) |           td5.appendChild(a4); | ||||||
|         td5.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1) |           td5.setAttribute("data-sort", d.link.distance !== undefined ? -d.link.distance : 1); | ||||||
|         tr.appendChild(td5) |           tr.appendChild(td5); | ||||||
| 
 | 
 | ||||||
|         tbody.appendChild(tr) |           tbody.appendChild(tr); | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|       table.appendChild(tbody) |         table.appendChild(tbody); | ||||||
|       table.className = "node-links" |         table.className = "node-links"; | ||||||
| 
 | 
 | ||||||
|       new Tablesort(table) |         Tablesort(table); | ||||||
| 
 | 
 | ||||||
|       el.appendChild(table) |         el.appendChild(table); | ||||||
|       } |       } | ||||||
|   } |     }; | ||||||
| }) |   }); | ||||||
|  |  | ||||||
|  | @ -1,41 +1,43 @@ | ||||||
| define(function () { | define(function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function () { |   return function () { | ||||||
|     var self = this |     var self = this; | ||||||
| 
 | 
 | ||||||
|     self.render = function (el) { |     self.render = function (el) { | ||||||
|       var p = document.createElement("p") |       var p = document.createElement("p"); | ||||||
|       p.setAttribute("class", "legend") |       p.setAttribute("class", "legend"); | ||||||
|       el.appendChild(p) |       el.appendChild(p); | ||||||
| 
 | 
 | ||||||
|       var spanNew = document.createElement("span") |       var spanNew = document.createElement("span"); | ||||||
|       spanNew.setAttribute("class", "legend-new") |       spanNew.setAttribute("class", "legend-new"); | ||||||
|       var symbolNew = document.createElement("span") |       var symbolNew = document.createElement("span"); | ||||||
|       symbolNew.setAttribute("class", "symbol") |       symbolNew.setAttribute("class", "symbol"); | ||||||
|       var textNew = document.createTextNode(" Neuer Knoten") |       var textNew = document.createTextNode(" Neuer Knoten"); | ||||||
|       spanNew.appendChild(symbolNew) |       spanNew.appendChild(symbolNew); | ||||||
|       spanNew.appendChild(textNew) |       spanNew.appendChild(textNew); | ||||||
|       p.appendChild(spanNew) |       p.appendChild(spanNew); | ||||||
| 
 | 
 | ||||||
|       var spanOnline = document.createElement("span") |       var spanOnline = document.createElement("span"); | ||||||
|       spanOnline.setAttribute("class", "legend-online") |       spanOnline.setAttribute("class", "legend-online"); | ||||||
|       var symbolOnline = document.createElement("span") |       var symbolOnline = document.createElement("span"); | ||||||
|       symbolOnline.setAttribute("class", "symbol") |       symbolOnline.setAttribute("class", "symbol"); | ||||||
|       var textOnline = document.createTextNode(" Knoten ist online") |       var textOnline = document.createTextNode(" Knoten ist online"); | ||||||
|       spanOnline.appendChild(symbolOnline) |       spanOnline.appendChild(symbolOnline); | ||||||
|       spanOnline.appendChild(textOnline) |       spanOnline.appendChild(textOnline); | ||||||
|       p.appendChild(spanOnline) |       p.appendChild(spanOnline); | ||||||
| 
 | 
 | ||||||
|       var spanOffline = document.createElement("span") |       var spanOffline = document.createElement("span"); | ||||||
|       spanOffline.setAttribute("class", "legend-offline") |       spanOffline.setAttribute("class", "legend-offline"); | ||||||
|       var symbolOffline = document.createElement("span") |       var symbolOffline = document.createElement("span"); | ||||||
|       symbolOffline.setAttribute("class", "symbol") |       symbolOffline.setAttribute("class", "symbol"); | ||||||
|       var textOffline = document.createTextNode(" Knoten ist offline") |       var textOffline = document.createTextNode(" Knoten ist offline"); | ||||||
|       spanOffline.appendChild(symbolOffline) |       spanOffline.appendChild(symbolOffline); | ||||||
|       spanOffline.appendChild(textOffline) |       spanOffline.appendChild(textOffline); | ||||||
|       p.appendChild(spanOffline) |       p.appendChild(spanOffline); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     return self |     return self; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,60 +1,68 @@ | ||||||
| define(["sorttable", "virtual-dom"], function (SortTable, V) { | define(["sorttable", "virtual-dom", "helper"], function (SortTable, V, helper) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   function linkName(d) { |   function linkName(d) { | ||||||
|     return (d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + 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", | ||||||
|     sort: function (a, b) { |     sort: function (a, b) { | ||||||
|                       return linkName(a).localeCompare(linkName(b)) |       return linkName(a).localeCompare(linkName(b)); | ||||||
|     }, |     }, | ||||||
|     reverse: false |     reverse: false | ||||||
|   }, |   }, | ||||||
|                   { name: "TQ", |     { | ||||||
|                     sort: function (a, b) { return a.tq - b.tq}, |       name: "TQ", | ||||||
|  |       sort: function (a, b) { | ||||||
|  |         return a.tq - b.tq; | ||||||
|  |       }, | ||||||
|       reverse: true |       reverse: true | ||||||
|     }, |     }, | ||||||
|                   { name: "Typ", |     { | ||||||
|  |       name: "Typ", | ||||||
|       sort: function (a, b) { |       sort: function (a, b) { | ||||||
|                       return a.type.localeCompare(b.type) |         return a.type.localeCompare(b.type); | ||||||
|       }, |       }, | ||||||
|       reverse: false |       reverse: false | ||||||
|     }, |     }, | ||||||
|                   { name: "Entfernung", |     { | ||||||
|  |       name: "Entfernung", | ||||||
|       sort: function (a, b) { |       sort: function (a, b) { | ||||||
|         return (a.distance === undefined ? -1 : a.distance) - |         return (a.distance === undefined ? -1 : a.distance) - | ||||||
|                              (b.distance === undefined ? -1 : b.distance) |           (b.distance === undefined ? -1 : b.distance); | ||||||
|       }, |       }, | ||||||
|       reverse: true |       reverse: true | ||||||
|                   }] |     }]; | ||||||
| 
 | 
 | ||||||
|   return function (linkScale, router) { |   return function (linkScale, router) { | ||||||
|     var table = new SortTable(headings, 2, renderRow) |     var table = new SortTable(headings, 2, renderRow); | ||||||
| 
 | 
 | ||||||
|     function renderRow(d) { |     function renderRow(d) { | ||||||
|       var td1Content = [V.h("a", {href: "#", onclick: router.link(d)}, linkName(d))] |       var td1Content = [V.h("a", {href: "#", onclick: router.link(d)}, linkName(d))]; | ||||||
| 
 | 
 | ||||||
|       var td1 = V.h("td", td1Content) |       var td1 = V.h("td", td1Content); | ||||||
|       var td2 = V.h("td", {style: {color: linkScale(d.tq).hex()}}, showTq(d)) |       var td2 = V.h("td", {style: {color: linkScale(d.tq).hex()}}, helper.showTq(d)); | ||||||
|       var td3 = V.h("td", d.type) |       var td3 = V.h("td", d.type); | ||||||
|       var td4 = V.h("td", showDistance(d)) |       var td4 = V.h("td", helper.showDistance(d)); | ||||||
| 
 | 
 | ||||||
|       return V.h("tr", [td1, td2, td3, td4]) |       return V.h("tr", [td1, td2, td3, td4]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.render = function (d) { |     this.render = function (d) { | ||||||
|       var el = document.createElement("div") |       var el = document.createElement("div"); | ||||||
|       el.last = V.h("div") |       el.last = V.h("div"); | ||||||
|       d.appendChild(el) |       d.appendChild(el); | ||||||
| 
 | 
 | ||||||
|       var h2 = document.createElement("h2") |       var h2 = document.createElement("h2"); | ||||||
|       h2.textContent = "Verbindungen" |       h2.textContent = "Verbindungen"; | ||||||
|       el.appendChild(h2) |       el.appendChild(h2); | ||||||
| 
 | 
 | ||||||
|       el.appendChild(table.el) |       el.appendChild(table.el); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     this.setData = function (d) { |     this.setData = function (d) { | ||||||
|       table.setData(d.graph.links) |       table.setData(d.graph.links); | ||||||
|     } |     }; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| define(["leaflet"], function (L) { | define(["leaflet"], function (L) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return L.CircleMarker.extend({ |   return L.CircleMarker.extend({ | ||||||
|     outerCircle: { |     outerCircle: { | ||||||
|       stroke: false, |       stroke: false, | ||||||
|  | @ -30,30 +32,30 @@ define(["leaflet"], function (L) { | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     initialize: function (latlng) { |     initialize: function (latlng) { | ||||||
|       this.accuracyCircle = L.circle(latlng, 0, this.accuracyCircle) |       this.accuracyCircle = L.circle(latlng, 0, this.accuracyCircle); | ||||||
|       this.outerCircle = L.circleMarker(latlng, this.outerCircle) |       this.outerCircle = L.circleMarker(latlng, this.outerCircle); | ||||||
|       L.CircleMarker.prototype.initialize.call(this, latlng, this.innerCircle) |       L.CircleMarker.prototype.initialize.call(this, latlng, this.innerCircle); | ||||||
| 
 | 
 | ||||||
|       this.on("remove", function () { |       this.on("remove", function () { | ||||||
|         this._map.removeLayer(this.accuracyCircle) |         this._map.removeLayer(this.accuracyCircle); | ||||||
|         this._map.removeLayer(this.outerCircle) |         this._map.removeLayer(this.outerCircle); | ||||||
|       }) |       }); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     setLatLng: function (latlng) { |     setLatLng: function (latlng) { | ||||||
|       this.accuracyCircle.setLatLng(latlng) |       this.accuracyCircle.setLatLng(latlng); | ||||||
|       this.outerCircle.setLatLng(latlng) |       this.outerCircle.setLatLng(latlng); | ||||||
|       L.CircleMarker.prototype.setLatLng.call(this, latlng) |       L.CircleMarker.prototype.setLatLng.call(this, latlng); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     setAccuracy: function (accuracy) { |     setAccuracy: function (accuracy) { | ||||||
|       this.accuracyCircle.setRadius(accuracy) |       this.accuracyCircle.setRadius(accuracy); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     onAdd: function (map) { |     onAdd: function (map) { | ||||||
|       this.accuracyCircle.addTo(map).bringToBack() |       this.accuracyCircle.addTo(map).bringToBack(); | ||||||
|       this.outerCircle.addTo(map) |       this.outerCircle.addTo(map); | ||||||
|       L.CircleMarker.prototype.onAdd.call(this, map) |       L.CircleMarker.prototype.onAdd.call(this, map); | ||||||
|     } |     } | ||||||
|   }) |   }); | ||||||
| }) | }); | ||||||
|  |  | ||||||
							
								
								
									
										276
									
								
								lib/main.js
									
										
									
									
									
								
							
							
						
						
									
										276
									
								
								lib/main.js
									
										
									
									
									
								
							|  | @ -1,182 +1,192 @@ | ||||||
| define(["moment", "router", "leaflet", "gui", "numeral"], | define(["moment", "router", "leaflet", "gui", "helper", "moment.de"], | ||||||
| function (moment, Router, L, GUI, numeral) { |   function (moment, Router, L, GUI, helper) { | ||||||
|  |     "use strict"; | ||||||
|  | 
 | ||||||
|     return function (config) { |     return function (config) { | ||||||
|       function handleData(data) { |       function handleData(data) { | ||||||
|       var dataNodes = {} |         var dataNodes = {}; | ||||||
|       dataNodes.nodes = [] |         dataNodes.nodes = []; | ||||||
|       dataNodes.nodeIds = [] |         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; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for (var i = 0; i < data.length; ++i) { |         for (var i = 0; i < data.length; ++i) { | ||||||
|         var vererr |           var vererr; | ||||||
|         if(i % 2) |           if (i % 2) { | ||||||
|             if (data[i].version !== 1) { |             if (data[i].version !== 1) { | ||||||
|             vererr = "Unsupported graph version: " + data[i].version |               vererr = "Unsupported graph version: " + data[i].version; | ||||||
|             console.log(vererr) //silent fail
 |               console.error(vererr); //silent fail
 | ||||||
|             } else { |             } else { | ||||||
|             data[i].batadv.links.forEach(rearrangeLinks) |               data[i].batadv.links.forEach(rearrangeLinks); | ||||||
|             dataGraph.batadv.nodes = dataGraph.batadv.nodes.concat(data[i].batadv.nodes) |               dataGraph.batadv.nodes = dataGraph.batadv.nodes.concat(data[i].batadv.nodes); | ||||||
|             dataGraph.batadv.links = dataGraph.batadv.links.concat(data[i].batadv.links) |               dataGraph.batadv.links = dataGraph.batadv.links.concat(data[i].batadv.links); | ||||||
|             dataGraph.timestamp = data[i].timestamp |               dataGraph.timestamp = data[i].timestamp; | ||||||
|             } |             } | ||||||
|         else |           } else if (data[i].version !== 2) { | ||||||
|           if (data[i].version !== 2) { |             vererr = "Unsupported nodes version: " + data[i].version; | ||||||
|             vererr = "Unsupported nodes version: " + data[i].version |             console.error(vererr); //silent fail
 | ||||||
|             console.log(vererr) //silent fail
 |  | ||||||
|           } else { |           } else { | ||||||
|             data[i].nodes.forEach(fillData) |             data[i].nodes.forEach(fillData); | ||||||
|             dataNodes.timestamp = data[i].timestamp |             dataNodes.timestamp = data[i].timestamp; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         function fillData(node) { |         function fillData(node) { | ||||||
|         var position = dataNodes.nodeIds.indexOf(node.nodeinfo.node_id) |           var position = dataNodes.nodeIds.indexOf(node.nodeinfo.node_id); | ||||||
|           if (position === -1) { |           if (position === -1) { | ||||||
|           dataNodes.nodes.push(node) |             dataNodes.nodes.push(node); | ||||||
|           dataNodes.nodeIds.push(node.nodeinfo.node_id) |             dataNodes.nodeIds.push(node.nodeinfo.node_id); | ||||||
|  |           } | ||||||
|  |           else if (node.flags.online === true) { | ||||||
|  |             dataNodes.nodes[position] = node; | ||||||
|           } |           } | ||||||
|         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; | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|         nodes.forEach(function (node) { |         nodes.forEach(function (node) { | ||||||
|         node.firstseen = moment.utc(node.firstseen).local() |           node.firstseen = moment.utc(node.firstseen).local(); | ||||||
|         node.lastseen = moment.utc(node.lastseen).local() |           node.lastseen = moment.utc(node.lastseen).local(); | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|       var now = moment() |         var now = moment(); | ||||||
|       var age = moment(now).subtract(config.maxAge, "days") |         var age = moment(now).subtract(config.maxAge, "days"); | ||||||
| 
 | 
 | ||||||
|       var newnodes = limit("firstseen", age, sortByKey("firstseen", nodes).filter(online)) |         var newnodes = helper.limit("firstseen", age, helper.sortByKey("firstseen", nodes).filter(helper.online)); | ||||||
|       var lostnodes = limit("lastseen", age, sortByKey("lastseen", nodes).filter(offline)) |         var lostnodes = helper.limit("lastseen", age, helper.sortByKey("lastseen", nodes).filter(helper.offline)); | ||||||
| 
 | 
 | ||||||
|       var graphnodes = {} |         var graphnodes = {}; | ||||||
| 
 | 
 | ||||||
|         dataNodes.nodes.forEach(function (d) { |         dataNodes.nodes.forEach(function (d) { | ||||||
|         graphnodes[d.nodeinfo.node_id] = d |           graphnodes[d.nodeinfo.node_id] = d; | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|       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) { |             if (d.unseen) { | ||||||
|             d.node.flags.online = true |               d.node.flags.online = true; | ||||||
|             d.node.flags.unseen = true |               d.node.flags.unseen = true; | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|         graph.links.forEach(function (d) { |         graph.links.forEach(function (d) { | ||||||
|         d.source = graph.nodes[d.source] |           d.source = graph.nodes[d.source]; | ||||||
| 
 | 
 | ||||||
|         if (graph.nodes[d.target].node) |           if (graph.nodes[d.target].node) { | ||||||
|           d.target = graph.nodes[d.target] |             d.target = graph.nodes[d.target]; | ||||||
|         else |           } else { | ||||||
|           d.target = undefined |             d.target = undefined; | ||||||
|       }) |           } | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         var links = graph.links.filter(function (d) { |         var links = graph.links.filter(function (d) { | ||||||
|         return d.target !== undefined |           return d.target !== undefined; | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|         links.forEach(function (d) { |         links.forEach(function (d) { | ||||||
|         var unknown = (d.source.node === undefined) |           var unknown = (d.source.node === undefined); | ||||||
|         var ids |           var ids; | ||||||
|         if (unknown) |           if (unknown) { | ||||||
|           ids = [d.source.id.replace(/:/g, ""), d.target.node.nodeinfo.node_id] |             ids = [d.source.id.replace(/:/g, ""), d.target.node.nodeinfo.node_id]; | ||||||
|         else |           } else { | ||||||
|           ids = [d.source.node.nodeinfo.node_id, d.target.node.nodeinfo.node_id] |             ids = [d.source.node.nodeinfo.node_id, d.target.node.nodeinfo.node_id]; | ||||||
|         d.id = ids.join("-") |           } | ||||||
|  |           d.id = ids.join("-"); | ||||||
| 
 | 
 | ||||||
|         if (unknown || |           if (unknown || !d.source.node.nodeinfo.location || !d.target.node.nodeinfo.location || | ||||||
|             !d.source.node.nodeinfo.location || |  | ||||||
|             !d.target.node.nodeinfo.location || |  | ||||||
|             isNaN(d.source.node.nodeinfo.location.latitude) || |             isNaN(d.source.node.nodeinfo.location.latitude) || | ||||||
|             isNaN(d.source.node.nodeinfo.location.longitude) || |             isNaN(d.source.node.nodeinfo.location.longitude) || | ||||||
|             isNaN(d.target.node.nodeinfo.location.latitude) || |             isNaN(d.target.node.nodeinfo.location.latitude) || | ||||||
|             isNaN(d.target.node.nodeinfo.location.longitude)) |             isNaN(d.target.node.nodeinfo.location.longitude)) { | ||||||
|           return |             return; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         d.latlngs = [] |           d.latlngs = []; | ||||||
|         d.latlngs.push(L.latLng(d.source.node.nodeinfo.location.latitude, d.source.node.nodeinfo.location.longitude)) |           d.latlngs.push(L.latLng(d.source.node.nodeinfo.location.latitude, d.source.node.nodeinfo.location.longitude)); | ||||||
|         d.latlngs.push(L.latLng(d.target.node.nodeinfo.location.latitude, d.target.node.nodeinfo.location.longitude)) |           d.latlngs.push(L.latLng(d.target.node.nodeinfo.location.latitude, d.target.node.nodeinfo.location.longitude)); | ||||||
| 
 | 
 | ||||||
|         d.distance = d.latlngs[0].distanceTo(d.latlngs[1]) |           d.distance = d.latlngs[0].distanceTo(d.latlngs[1]); | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|         nodes.forEach(function (d) { |         nodes.forEach(function (d) { | ||||||
|         d.neighbours = [] |           d.neighbours = []; | ||||||
|           if (d.statistics) { |           if (d.statistics) { | ||||||
|             /*eslint camelcase:0*/ |             /*eslint camelcase:0*/ | ||||||
|           if ("gateway" in d.statistics && d.statistics.gateway in graphnodes) |             if ("gateway" in d.statistics && d.statistics.gateway in graphnodes) { | ||||||
|             d.statistics.gateway = {"node": graphnodes[d.statistics.gateway], "id": d.statistics.gateway} |               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} |  | ||||||
|             } |             } | ||||||
|       }) |             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) { |           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") { | ||||||
|           d.type = "fastd" |             d.type = "fastd"; | ||||||
|           d.isVPN = true |             d.isVPN = true; | ||||||
|           } else if (d.type === "l2tp") { |           } else if (d.type === "l2tp") { | ||||||
|           d.type = "L2TP" |             d.type = "L2TP"; | ||||||
|           d.isVPN = true |             d.isVPN = true; | ||||||
|           } else if (d.type === "gre") { |           } else if (d.type === "gre") { | ||||||
|           d.type = "GRE" |             d.type = "GRE"; | ||||||
|           d.isVPN = true |             d.isVPN = true; | ||||||
|           } else if (d.type === "wireless") { |           } else if (d.type === "wireless") { | ||||||
|           d.type = "Wifi" |             d.type = "Wifi"; | ||||||
|           d.isVPN = false |             d.isVPN = false; | ||||||
|           } else if (d.type === "other") { |           } else if (d.type === "other") { | ||||||
|           d.type = "Kabel" |             d.type = "Kabel"; | ||||||
|           d.isVPN = false |             d.isVPN = false; | ||||||
|           } else { |           } else { | ||||||
|           d.type = "N/A" |             d.type = "N/A"; | ||||||
|           d.isVPN = false |             d.isVPN = false; | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|         if (d.isVPN && d.target.node) |           if (d.isVPN && d.target.node) { | ||||||
|           d.target.node.flags.uplink = true |             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}); | ||||||
|           return |             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.isVPN) |           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; | ||||||
|       }) |           } | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         links.sort(function (a, b) { |         links.sort(function (a, b) { | ||||||
|         return b.tq - a.tq |           return b.tq - a.tq; | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|       return { now: now, |         return { | ||||||
|  |           now: now, | ||||||
|           timestamp: moment.utc(dataNodes.timestamp).local(), |           timestamp: moment.utc(dataNodes.timestamp).local(), | ||||||
|           nodes: { |           nodes: { | ||||||
|             all: nodes, |             all: nodes, | ||||||
|  | @ -187,46 +197,46 @@ function (moment, Router, L, GUI, numeral) { | ||||||
|             links: links, |             links: links, | ||||||
|             nodes: graph.nodes |             nodes: graph.nodes | ||||||
|           } |           } | ||||||
|              } |         }; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     numeral.language("de") |       moment.locale("de"); | ||||||
|     moment.locale("de") |  | ||||||
| 
 | 
 | ||||||
|     var router = new Router() |       var router = new Router(); | ||||||
| 
 | 
 | ||||||
|     var urls = [] |       var urls = []; | ||||||
| 
 | 
 | ||||||
|     if (typeof config.dataPath === "string" || config.dataPath instanceof String) |       if (typeof config.dataPath === "string" || config.dataPath instanceof String) { | ||||||
|       config.dataPath = [config.dataPath] |         config.dataPath = [config.dataPath]; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       for (var i in config.dataPath) { |       for (var i in config.dataPath) { | ||||||
|       urls.push(config.dataPath[i] + "nodes.json") |         urls.push(config.dataPath[i] + "nodes.json"); | ||||||
|       urls.push(config.dataPath[i] + "graph.json") |         urls.push(config.dataPath[i] + "graph.json"); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function update() { |       function update() { | ||||||
|       return Promise.all(urls.map(getJSON)) |         return Promise.all(urls.map(helper.getJSON)) | ||||||
|                     .then(handleData) |           .then(handleData); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       update() |       update() | ||||||
|         .then(function (d) { |         .then(function (d) { | ||||||
|         var gui = new GUI(config, router) |           var gui = new GUI(config, router); | ||||||
|         gui.setData(d) |           gui.setData(d); | ||||||
|         router.setData(d) |           router.setData(d); | ||||||
|         router.start() |           router.start(); | ||||||
| 
 | 
 | ||||||
|           window.setInterval(function () { |           window.setInterval(function () { | ||||||
|             update().then(function (d) { |             update().then(function (d) { | ||||||
|             gui.setData(d) |               gui.setData(d); | ||||||
|             router.setData(d) |               router.setData(d); | ||||||
|           }) |             }); | ||||||
|         }, 60000) |           }, 60000); | ||||||
|         }) |         }) | ||||||
|         .catch(function (e) { |         .catch(function (e) { | ||||||
|         document.body.textContent = e |           document.body.textContent = e; | ||||||
|         console.log(e) |           console.warn(e); | ||||||
|       }) |         }); | ||||||
|   } |     }; | ||||||
| }) |   }); | ||||||
|  |  | ||||||
							
								
								
									
										690
									
								
								lib/map.js
									
										
									
									
									
								
							
							
						
						
									
										690
									
								
								lib/map.js
									
										
									
									
									
								
							|  | @ -1,10 +1,13 @@ | ||||||
| define(["map/clientlayer", "map/labelslayer", | define(["map/clientlayer", "map/labelslayer", | ||||||
|         "d3", "leaflet", "moment", "locationmarker", "rbush", |     "d3", "leaflet", "moment", "locationmarker", "rbush", "helper", | ||||||
|         "leaflet.label", "leaflet.providers"], |     "leaflet.label", "leaflet.providers", "moment.de"], | ||||||
|   function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush) { |   function (ClientLayer, LabelsLayer, d3, L, moment, LocationMarker, rbush, helper) { | ||||||
|     var options = { worldCopyJump: true, |     "use strict"; | ||||||
|  | 
 | ||||||
|  |     var options = { | ||||||
|  |       worldCopyJump: true, | ||||||
|       zoomControl: false |       zoomControl: false | ||||||
|                   } |     }; | ||||||
| 
 | 
 | ||||||
|     var AddLayerButton = L.Control.extend({ |     var AddLayerButton = L.Control.extend({ | ||||||
|       options: { |       options: { | ||||||
|  | @ -12,24 +15,24 @@ define(["map/clientlayer", "map/labelslayer", | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       initialize: function (f, options) { |       initialize: function (f, options) { | ||||||
|           L.Util.setOptions(this, options) |         L.Util.setOptions(this, options); | ||||||
|           this.f = f |         this.f = f; | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       onAdd: function () { |       onAdd: function () { | ||||||
|           var button = L.DomUtil.create("button", "add-layer") |         var button = L.DomUtil.create("button", "add-layer"); | ||||||
|           button.textContent = "\uF2C7" |         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
 | ||||||
|         // location picking mode; instead propagation is stopped in onClick().
 |         // location picking mode; instead propagation is stopped in onClick().
 | ||||||
|           L.DomEvent.addListener(button, "click", this.f, this) |         L.DomEvent.addListener(button, "click", this.f, this); | ||||||
| 
 | 
 | ||||||
|           this.button = button |         this.button = button; | ||||||
| 
 | 
 | ||||||
|           return button |         return button; | ||||||
|       } |       } | ||||||
|     }) |     }); | ||||||
| 
 | 
 | ||||||
|     var LocateButton = L.Control.extend({ |     var LocateButton = L.Control.extend({ | ||||||
|       options: { |       options: { | ||||||
|  | @ -40,35 +43,35 @@ define(["map/clientlayer", "map/labelslayer", | ||||||
|       button: undefined, |       button: undefined, | ||||||
| 
 | 
 | ||||||
|       initialize: function (f, options) { |       initialize: function (f, options) { | ||||||
|           L.Util.setOptions(this, options) |         L.Util.setOptions(this, options); | ||||||
|           this.f = f |         this.f = f; | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       onAdd: function () { |       onAdd: function () { | ||||||
|           var button = L.DomUtil.create("button", "locate-user") |         var button = L.DomUtil.create("button", "locate-user"); | ||||||
|           button.textContent = "\uF2E9" |         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); | ||||||
| 
 | 
 | ||||||
|           this.button = button |         this.button = button; | ||||||
| 
 | 
 | ||||||
|           return button |         return button; | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       update: function () { |       update: function () { | ||||||
|           this.button.classList.toggle("active", this.active) |         this.button.classList.toggle("active", this.active); | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       set: function (v) { |       set: function (v) { | ||||||
|           this.active = v |         this.active = v; | ||||||
|           this.update() |         this.update(); | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       onClick: function () { |       onClick: function () { | ||||||
|           this.f(!this.active) |         this.f(!this.active); | ||||||
|       } |       } | ||||||
|     }) |     }); | ||||||
| 
 | 
 | ||||||
|     var CoordsPickerButton = L.Control.extend({ |     var CoordsPickerButton = L.Control.extend({ | ||||||
|       options: { |       options: { | ||||||
|  | @ -79,470 +82,547 @@ define(["map/clientlayer", "map/labelslayer", | ||||||
|       button: undefined, |       button: undefined, | ||||||
| 
 | 
 | ||||||
|       initialize: function (f, options) { |       initialize: function (f, options) { | ||||||
|         L.Util.setOptions(this, options) |         L.Util.setOptions(this, options); | ||||||
|         this.f = f |         this.f = f; | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       onAdd: function () { |       onAdd: function () { | ||||||
|         var button = L.DomUtil.create("button", "coord-picker") |         var button = L.DomUtil.create("button", "coord-picker"); | ||||||
|         button.textContent = "\uF2A6" |         button.textContent = "\uF3A3"; | ||||||
| 
 | 
 | ||||||
|         // 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().
 | ||||||
|         L.DomEvent.addListener(button, "click", this.onClick, this) |         L.DomEvent.addListener(button, "click", this.onClick, this); | ||||||
| 
 | 
 | ||||||
|         this.button = button |         this.button = button; | ||||||
| 
 | 
 | ||||||
|         return button |         return button; | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       update: function () { |       update: function () { | ||||||
|         this.button.classList.toggle("active", this.active) |         this.button.classList.toggle("active", this.active); | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       set: function (v) { |       set: function (v) { | ||||||
|         this.active = v |         this.active = v; | ||||||
|         this.update() |         this.update(); | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       onClick: function (e) { |       onClick: function (e) { | ||||||
|         L.DomEvent.stopPropagation(e) |         L.DomEvent.stopPropagation(e); | ||||||
|         this.f(!this.active) |         this.f(!this.active); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     }) |     }); | ||||||
| 
 | 
 | ||||||
|     function mkMarker(dict, iconFunc, router) { |     function mkMarker(dict, iconFunc, router) { | ||||||
|       return function (d) { |       return function (d) { | ||||||
|         var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], iconFunc(d)) |         var m = L.circleMarker([d.nodeinfo.location.latitude, d.nodeinfo.location.longitude], iconFunc(d)); | ||||||
| 
 | 
 | ||||||
|         m.resetStyle = function () { |         m.resetStyle = function () { | ||||||
|           m.setStyle(iconFunc(d)) |           m.setStyle(iconFunc(d)); | ||||||
|         } |         }; | ||||||
| 
 | 
 | ||||||
|         m.on("click", router.node(d)) |         m.on("click", router.node(d)); | ||||||
|         m.bindLabel(d.nodeinfo.hostname) |         m.bindLabel(d.nodeinfo.hostname); | ||||||
| 
 | 
 | ||||||
|         dict[d.nodeinfo.node_id] = m |         dict[d.nodeinfo.node_id] = m; | ||||||
| 
 | 
 | ||||||
|         return m |         return m; | ||||||
|       } |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function addLinksToMap(dict, linkScale, graph, router) { |     function addLinksToMap(dict, linkScale, graph, router) { | ||||||
|       graph = graph.filter(function (d) { |       graph = graph.filter(function (d) { | ||||||
|         return "distance" in d && d.type !== "VPN" |         return "distance" in d && d.type !== "VPN"; | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       var lines = graph.map( function (d) { |       return graph.map(function (d) { | ||||||
|         var opts = { color: d.type === "Kabel" ? "#50B0F0" : linkScale(d.tq).hex(), |         var opts = { | ||||||
|  |           color: d.type === "Kabel" ? "#50B0F0" : linkScale(d.tq).hex(), | ||||||
|           weight: 4, |           weight: 4, | ||||||
|           opacity: 0.5, |           opacity: 0.5, | ||||||
|           dashArray: "none" |           dashArray: "none" | ||||||
|                    } |         }; | ||||||
| 
 | 
 | ||||||
|         var line = L.polyline(d.latlngs, opts) |         var line = L.polyline(d.latlngs, opts); | ||||||
| 
 | 
 | ||||||
|         line.resetStyle = function () { |         line.resetStyle = function () { | ||||||
|           line.setStyle(opts) |           line.setStyle(opts); | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         line.bindLabel(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "<br><strong>" + helper.showDistance(d) + " / " + helper.showTq(d) + "</strong>"); | ||||||
|  |         line.on("click", router.link(d)); | ||||||
|  | 
 | ||||||
|  |         dict[d.id] = line; | ||||||
|  | 
 | ||||||
|  |         return line; | ||||||
|  |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         line.bindLabel(d.source.node.nodeinfo.hostname + " – " + d.target.node.nodeinfo.hostname + "<br><strong>" + showDistance(d) + " / " + showTq(d) + "</strong>") |     var iconOnline = { | ||||||
|         line.on("click", router.link(d)) |       color: "#1566A9", | ||||||
| 
 |       fillColor: "#1566A9", | ||||||
|         dict[d.id] = line |       radius: 6, | ||||||
| 
 |       fillOpacity: 0.5, | ||||||
|         return line |       opacity: 0.5, | ||||||
|       }) |       weight: 2, | ||||||
| 
 |       className: "stroke-first" | ||||||
|       return lines |     }; | ||||||
|     } |     var iconOffline = { | ||||||
| 
 |       color: "#D43E2A", | ||||||
|     var iconOnline  = { color: "#1566A9", fillColor: "#1566A9", radius: 6, fillOpacity: 0.5, opacity: 0.5, weight: 2, className: "stroke-first" } |       fillColor: "#D43E2A", | ||||||
|     var iconOffline = { color: "#D43E2A", fillColor: "#D43E2A", radius: 3, fillOpacity: 0.5, opacity: 0.5, weight: 1, className: "stroke-first" } |       radius: 3, | ||||||
|     var iconLost    = { color: "#D43E2A", fillColor: "#D43E2A", radius: 6, fillOpacity: 0.8, opacity: 0.8, weight: 1, className: "stroke-first" } |       fillOpacity: 0.5, | ||||||
|     var iconAlert   = { color: "#D43E2A", fillColor: "#D43E2A", radius: 6, fillOpacity: 0.8, opacity: 0.8, weight: 2, className: "stroke-first node-alert" } |       opacity: 0.5, | ||||||
|     var iconNew     = { color: "#1566A9", fillColor: "#93E929", radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2 } |       weight: 1, | ||||||
|  |       className: "stroke-first" | ||||||
|  |     }; | ||||||
|  |     var iconLost = { | ||||||
|  |       color: "#D43E2A", | ||||||
|  |       fillColor: "#D43E2A", | ||||||
|  |       radius: 4, | ||||||
|  |       fillOpacity: 0.8, | ||||||
|  |       opacity: 0.8, | ||||||
|  |       weight: 1, | ||||||
|  |       className: "stroke-first" | ||||||
|  |     }; | ||||||
|  |     var iconAlert = { | ||||||
|  |       color: "#D43E2A", | ||||||
|  |       fillColor: "#D43E2A", | ||||||
|  |       radius: 5, | ||||||
|  |       fillOpacity: 0.8, | ||||||
|  |       opacity: 0.8, | ||||||
|  |       weight: 2, | ||||||
|  |       className: "stroke-first" | ||||||
|  |     }; | ||||||
|  |     var iconNew = {color: "#1566A9", fillColor: "#93E929", radius: 6, fillOpacity: 1.0, opacity: 0.5, weight: 2}; | ||||||
| 
 | 
 | ||||||
|     return function (config, linkScale, sidebar, router, buttons) { |     return function (config, linkScale, sidebar, router, buttons) { | ||||||
|       var self = this |       var self = this; | ||||||
|       var barycenter |       var barycenter; | ||||||
|       var groupOnline, groupOffline, groupNew, groupLost, groupLines |       var groupOnline, groupOffline, groupNew, groupLost, groupLines; | ||||||
|       var savedView |       var savedView; | ||||||
| 
 | 
 | ||||||
|       var map, userLocation |       var map, userLocation; | ||||||
|       var layerControl |       var layerControl; | ||||||
|       var customLayers = {} |       var customLayers = {}; | ||||||
|       var baseLayers = {} |       var baseLayers = {}; | ||||||
| 
 | 
 | ||||||
|       var locateUserButton = new LocateButton(function (d) { |       var locateUserButton = new LocateButton(function (d) { | ||||||
|         if (d) |         if (d) { | ||||||
|           enableTracking() |           enableTracking(); | ||||||
|         else |         } else { | ||||||
|           disableTracking() |           disableTracking(); | ||||||
|       }) |         } | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|       var mybuttons = [] |       var mybuttons = []; | ||||||
| 
 | 
 | ||||||
|       function addButton(button) { |       function addButton(button) { | ||||||
|         var el = button.onAdd() |         var el = button.onAdd(); | ||||||
|         mybuttons.push(el) |         mybuttons.push(el); | ||||||
|         buttons.appendChild(el) |         buttons.appendChild(el); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function clearButtons() { |       function clearButtons() { | ||||||
|         mybuttons.forEach(function (d) { |         mybuttons.forEach(function (d) { | ||||||
|           buttons.removeChild(d) |           buttons.removeChild(d); | ||||||
|         }) |         }); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var showCoordsPickerButton = new CoordsPickerButton(function (d) { |       var showCoordsPickerButton = new CoordsPickerButton(function (d) { | ||||||
|         if (d) |         if (d) { | ||||||
|           enableCoords() |           enableCoords(); | ||||||
|         else |         } else { | ||||||
|           disableCoords() |           disableCoords(); | ||||||
|       }) |         } | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|       function saveView() { |       function saveView() { | ||||||
|         savedView = {center: map.getCenter(), |         savedView = { | ||||||
|                      zoom: map.getZoom()} |           center: map.getCenter(), | ||||||
|  |           zoom: map.getZoom() | ||||||
|  |         }; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function enableTracking() { |       function enableTracking() { | ||||||
|         map.locate({watch: true, |         map.locate({ | ||||||
|  |           watch: true, | ||||||
|           enableHighAccuracy: true, |           enableHighAccuracy: true, | ||||||
|           setView: true |           setView: true | ||||||
|                    }) |         }); | ||||||
|         locateUserButton.set(true) |         locateUserButton.set(true); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function disableTracking() { |       function disableTracking() { | ||||||
|         map.stopLocate() |         map.stopLocate(); | ||||||
|         locationError() |         locationError(); | ||||||
|         locateUserButton.set(false) |         locateUserButton.set(false); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function enableCoords() { |       function enableCoords() { | ||||||
|         map.getContainer().classList.add("pick-coordinates") |         map.getContainer().classList.add("pick-coordinates"); | ||||||
|         map.on("click", showCoordinates) |         map.on("click", showCoordinates); | ||||||
|         showCoordsPickerButton.set(true) |         showCoordsPickerButton.set(true); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function disableCoords() { |       function disableCoords() { | ||||||
|         map.getContainer().classList.remove("pick-coordinates") |         map.getContainer().classList.remove("pick-coordinates"); | ||||||
|         map.off("click", showCoordinates) |         map.off("click", showCoordinates); | ||||||
|         showCoordsPickerButton.set(false) |         showCoordsPickerButton.set(false); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function showCoordinates(e) { |       function showCoordinates(e) { | ||||||
|         router.gotoLocation(e.latlng) |         router.gotoLocation(e.latlng); | ||||||
|         // window.prompt("Koordinaten (Lat, Lng)", e.latlng.lat.toFixed(9) + ", " + e.latlng.lng.toFixed(9))
 |         // window.prompt("Koordinaten (Lat, Lng)", e.latlng.lat.toFixed(9) + ", " + e.latlng.lng.toFixed(9))
 | ||||||
|         disableCoords() |         disableCoords(); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function locationFound(e) { |       function locationFound(e) { | ||||||
|         if (!userLocation) |         if (!userLocation) { | ||||||
|           userLocation = new LocationMarker(e.latlng).addTo(map) |           userLocation = new LocationMarker(e.latlng).addTo(map); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         userLocation.setLatLng(e.latlng) |         userLocation.setLatLng(e.latlng); | ||||||
|         userLocation.setAccuracy(e.accuracy) |         userLocation.setAccuracy(e.accuracy); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function locationError() { |       function locationError() { | ||||||
|         if (userLocation) { |         if (userLocation) { | ||||||
|           map.removeLayer(userLocation) |           map.removeLayer(userLocation); | ||||||
|           userLocation = null |           userLocation = null; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function addLayer(layerName) { |       function addLayer(layerName) { | ||||||
|         if (layerName in baseLayers) |         if (layerName in baseLayers) { | ||||||
|           return |           return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (layerName in customLayers) |         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[layerName] = layer |           customLayers[layerName] = layer; | ||||||
| 
 | 
 | ||||||
|           if (localStorageTest()) |           if (helper.localStorageTest()) { | ||||||
|             localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))) |             localStorage.setItem("map/customLayers", JSON.stringify(Object.keys(customLayers))); | ||||||
|  |           } | ||||||
|         } catch (e) { |         } catch (e) { | ||||||
|           return |           console.error(e); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function contextMenuGotoLocation(e) { |       function contextMenuGotoLocation(e) { | ||||||
|         router.gotoLocation(e.latlng) |         router.gotoLocation(e.latlng); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var el = document.createElement("div") |       var el = document.createElement("div"); | ||||||
|       el.classList.add("map") |       el.classList.add("map"); | ||||||
| 
 | 
 | ||||||
|       map = L.map(el, options) |       map = L.map(el, options); | ||||||
| 
 | 
 | ||||||
|       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 ? "layers" in d.config ? L.tileLayer.wms(d.url, d.config) : L.tileLayer(d.url, d.config) : L.tileLayer.provider(d.name) |           "layer": "url" in d ? L.tileLayer(d.url, d.config) : L.tileLayer.provider(d.name) | ||||||
|         } |         }; | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       layers[0].layer.addTo(map) |       layers[0].layer.addTo(map); | ||||||
| 
 | 
 | ||||||
|       layers.forEach(function (d) { |       layers.forEach(function (d) { | ||||||
|         baseLayers[d.name] = d.layer |         baseLayers[d.name] = d.layer; | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       map.on("locationfound", locationFound) |       map.on("locationfound", locationFound); | ||||||
|       map.on("locationerror", locationError) |       map.on("locationerror", locationError); | ||||||
|       map.on("dragend", saveView) |       map.on("dragend", saveView); | ||||||
|       map.on("contextmenu", contextMenuGotoLocation) |       map.on("contextmenu", contextMenuGotoLocation); | ||||||
| 
 | 
 | ||||||
|       addButton(locateUserButton) |       addButton(locateUserButton); | ||||||
|       addButton(showCoordsPickerButton) |       addButton(showCoordsPickerButton); | ||||||
| 
 | 
 | ||||||
|       addButton(new AddLayerButton(function () { |       addButton(new AddLayerButton(function () { | ||||||
|         /*eslint no-alert:0*/ |         /*eslint no-alert:0*/ | ||||||
|         var layerName = prompt("Leaflet Provider:") |         var layerName = prompt("Leaflet Provider:"); | ||||||
|         addLayer(layerName) |         addLayer(layerName); | ||||||
|       })) |       })); | ||||||
| 
 | 
 | ||||||
|       layerControl = L.control.layers(baseLayers, [], {position: "bottomright"}) |       layerControl = L.control.layers(baseLayers, [], {position: "bottomright"}); | ||||||
|       layerControl.addTo(map) |       layerControl.addTo(map); | ||||||
| 
 | 
 | ||||||
|       if (localStorageTest()) { |       if (helper.localStorageTest()) { | ||||||
|         var d = JSON.parse(localStorage.getItem("map/customLayers")) |         var d = JSON.parse(localStorage.getItem("map/customLayers")); | ||||||
| 
 |  | ||||||
|         if (d) |  | ||||||
|           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) { |         if (d) { | ||||||
|           map.removeLayer(layers[0].layer) |           d.forEach(addLayer); | ||||||
|           map.addLayer(d) |         } | ||||||
|  | 
 | ||||||
|  |         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.on("baselayerchange", function (e) { | ||||||
|         map.options.maxZoom = e.layer.options.maxZoom |         map.options.maxZoom = e.layer.options.maxZoom; | ||||||
|         clientLayer.options.maxZoom = map.options.maxZoom |         clientLayer.options.maxZoom = map.options.maxZoom; | ||||||
|         labelsLayer.options.maxZoom = map.options.maxZoom |         labelsLayer.options.maxZoom = map.options.maxZoom; | ||||||
|         if (map.getZoom() > map.options.maxZoom) map.setZoom(map.options.maxZoom) |         if (map.getZoom() > map.options.maxZoom) { | ||||||
|         if (localStorageTest()) |           map.setZoom(map.options.maxZoom); | ||||||
|           localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name})) |         } | ||||||
|       }) |         if (helper.localStorageTest()) { | ||||||
|  |           localStorage.setItem("map/selectedLayer", JSON.stringify({name: e.name})); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|       var nodeDict = {} |       var nodeDict = {}; | ||||||
|       var linkDict = {} |       var linkDict = {}; | ||||||
|       var highlight |       var highlight; | ||||||
| 
 | 
 | ||||||
|       function resetMarkerStyles(nodes, links) { |       function resetMarkerStyles(nodes, links) { | ||||||
|         Object.keys(nodes).forEach(function (d) { |         Object.keys(nodes).forEach(function (d) { | ||||||
|           nodes[d].resetStyle() |           nodes[d].resetStyle(); | ||||||
|         }) |         }); | ||||||
| 
 | 
 | ||||||
|         Object.keys(links).forEach(function (d) { |         Object.keys(links).forEach(function (d) { | ||||||
|           links[d].resetStyle() |           links[d].resetStyle(); | ||||||
|         }) |         }); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function setView(bounds) { |       function setView(bounds) { | ||||||
|         map.fitBounds(bounds, {paddingTopLeft: [sidebar(), 0]}) |         map.fitBounds(bounds, {paddingTopLeft: [sidebar(), 0]}); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function resetZoom() { |       function resetZoom() { | ||||||
|         if (barycenter) |         if (barycenter) { | ||||||
|           setView(barycenter.getBounds()) |           setView(barycenter.getBounds()); | ||||||
|  |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function goto(m) { |       function goto(m) { | ||||||
|         var bounds |         var bounds; | ||||||
| 
 | 
 | ||||||
|         if ("getBounds" in m) |         if ("getBounds" in m) { | ||||||
|           bounds = m.getBounds() |           bounds = m.getBounds(); | ||||||
|         else |         } else { | ||||||
|           bounds = L.latLngBounds([m.getLatLng()]) |           bounds = L.latLngBounds([m.getLatLng()]); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         setView(bounds) |         setView(bounds); | ||||||
| 
 | 
 | ||||||
|         return m |         return m; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function updateView(nopanzoom) { |       function updateView(nopanzoom) { | ||||||
|         resetMarkerStyles(nodeDict, linkDict) |         resetMarkerStyles(nodeDict, linkDict); | ||||||
|         var m |         var m; | ||||||
| 
 | 
 | ||||||
|         if (highlight !== undefined) |         if (highlight !== undefined) { | ||||||
|           if (highlight.type === "node") { |           if (highlight.type === "node") { | ||||||
|             m = nodeDict[highlight.o.nodeinfo.node_id] |             m = nodeDict[highlight.o.nodeinfo.node_id]; | ||||||
| 
 | 
 | ||||||
|             if (m) |             if (m) { | ||||||
|               m.setStyle({ color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7, className: "stroke-first" }) |               m.setStyle({color: "orange", weight: 20, fillOpacity: 1, opacity: 0.7, className: "stroke-first"}); | ||||||
|  |             } | ||||||
|           } else if (highlight.type === "link") { |           } else if (highlight.type === "link") { | ||||||
|             m = linkDict[highlight.o.id] |             m = linkDict[highlight.o.id]; | ||||||
| 
 | 
 | ||||||
|             if (m) |             if (m) { | ||||||
|               m.setStyle({ weight: 7, opacity: 1, dashArray: "10, 10" }) |               m.setStyle({weight: 4, opacity: 1, dashArray: "5, 10"}); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!nopanzoom) |         if (!nopanzoom) { | ||||||
|           if (m) |           if (m) { | ||||||
|             goto(m) |             goto(m); | ||||||
|           else if (savedView) |           } else if (savedView) { | ||||||
|             map.setView(savedView.center, savedView.zoom) |             map.setView(savedView.center, savedView.zoom); | ||||||
|           else |           } else { | ||||||
|             resetZoom() |             resetZoom(); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function calcBarycenter(nodes) { |       function calcBarycenter(nodes) { | ||||||
|         nodes = nodes.map(function (d) { return d.nodeinfo.location }) |         nodes = nodes.map(function (d) { | ||||||
|  |           return d.nodeinfo.location; | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         if (nodes.length === 0) |         if (nodes.length === 0) { | ||||||
|           return undefined |           return undefined; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         var lats = nodes.map(function (d) { return d.latitude }) |         var lats = nodes.map(function (d) { | ||||||
|         var lngs = nodes.map(function (d) { return d.longitude }) |           return d.latitude; | ||||||
|  |         }); | ||||||
|  |         var lngs = nodes.map(function (d) { | ||||||
|  |           return d.longitude; | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         var barycenter = L.latLng(d3.median(lats), d3.median(lngs)) |         var barycenter = L.latLng(d3.median(lats), d3.median(lngs)); | ||||||
|         var barycenterDev = [d3.deviation(lats), d3.deviation(lngs)] |         var barycenterDev = [d3.deviation(lats), d3.deviation(lngs)]; | ||||||
| 
 | 
 | ||||||
|         if (barycenterDev[0] === undefined) |         if (barycenterDev[0] === undefined) { | ||||||
|           barycenterDev[0] = 0 |           barycenterDev[0] = 0; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (barycenterDev[1] === undefined) |         if (barycenterDev[1] === undefined) { | ||||||
|           barycenterDev[1] = 0 |           barycenterDev[1] = 0; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         var barycenterCircle = L.latLng(barycenter.lat + barycenterDev[0], |         var barycenterCircle = L.latLng(barycenter.lat + barycenterDev[0], | ||||||
|                                         barycenter.lng + barycenterDev[1]) |           barycenter.lng + barycenterDev[1]); | ||||||
| 
 | 
 | ||||||
|         var r = barycenter.distanceTo(barycenterCircle) |         var r = barycenter.distanceTo(barycenterCircle); | ||||||
| 
 | 
 | ||||||
|         return L.circle(barycenter, r * config.mapSigmaScale) |         return L.circle(barycenter, r * config.mapSigmaScale); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function mapRTree(d) { |       function mapRTree(d) { | ||||||
|         var o = [d.nodeinfo.location.latitude, d.nodeinfo.location.longitude, |         var o = [d.nodeinfo.location.latitude, d.nodeinfo.location.longitude, | ||||||
|                   d.nodeinfo.location.latitude, d.nodeinfo.location.longitude] |           d.nodeinfo.location.latitude, d.nodeinfo.location.longitude]; | ||||||
| 
 | 
 | ||||||
|         o.node = d |         o.node = d; | ||||||
| 
 | 
 | ||||||
|         return o |         return o; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       self.setData = function (data) { |       self.setData = function (data) { | ||||||
|         nodeDict = {} |         nodeDict = {}; | ||||||
|         linkDict = {} |         linkDict = {}; | ||||||
| 
 | 
 | ||||||
|         if (groupOffline) |         if (groupOffline) { | ||||||
|           groupOffline.clearLayers() |           groupOffline.clearLayers(); | ||||||
| 
 |  | ||||||
|         if (groupOnline) |  | ||||||
|           groupOnline.clearLayers() |  | ||||||
| 
 |  | ||||||
|         if (groupNew) |  | ||||||
|           groupNew.clearLayers() |  | ||||||
| 
 |  | ||||||
|         if (groupLost) |  | ||||||
|           groupLost.clearLayers() |  | ||||||
| 
 |  | ||||||
|         if (groupLines) |  | ||||||
|           groupLines.clearLayers() |  | ||||||
| 
 |  | ||||||
|         var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router) |  | ||||||
|         groupLines = L.featureGroup(lines).addTo(map) |  | ||||||
| 
 |  | ||||||
|         if (typeof config.fixedCenter === "undefined") |  | ||||||
|           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 nodesOffline = subtract(data.nodes.all.filter(offline), data.nodes.lost) |  | ||||||
| 
 |  | ||||||
|         var markersOnline = nodesOnline.filter(has_location) |  | ||||||
|           .map(mkMarker(nodeDict, function () { return iconOnline }, router)) |  | ||||||
| 
 |  | ||||||
|         var markersOffline = nodesOffline.filter(has_location) |  | ||||||
|           .map(mkMarker(nodeDict, function () { return iconOffline }, router)) |  | ||||||
| 
 |  | ||||||
|         var markersNew = data.nodes.new.filter(has_location) |  | ||||||
|           .map(mkMarker(nodeDict, function () { return iconNew }, router)) |  | ||||||
| 
 |  | ||||||
|         var markersLost = data.nodes.lost.filter(has_location) |  | ||||||
|           .map(mkMarker(nodeDict, function (d) { |  | ||||||
|             if (d.lastseen.isAfter(moment(data.now).subtract(3, "days"))) |  | ||||||
|               return iconAlert |  | ||||||
| 
 |  | ||||||
|             return iconLost |  | ||||||
|           }, router)) |  | ||||||
| 
 |  | ||||||
|         groupOffline = L.featureGroup(markersOffline).addTo(map) |  | ||||||
|         groupOnline = L.featureGroup(markersOnline).addTo(map) |  | ||||||
|         groupLost = L.featureGroup(markersLost).addTo(map) |  | ||||||
|         groupNew = L.featureGroup(markersNew).addTo(map) |  | ||||||
| 
 |  | ||||||
|         var rtreeOnlineAll = rbush(9) |  | ||||||
| 
 |  | ||||||
|         rtreeOnlineAll.load(data.nodes.all.filter(online).filter(has_location).map(mapRTree)) |  | ||||||
| 
 |  | ||||||
|         clientLayer.setData(rtreeOnlineAll) |  | ||||||
|         labelsLayer.setData({online: nodesOnline.filter(has_location), |  | ||||||
|                              offline: nodesOffline.filter(has_location), |  | ||||||
|                              new: data.nodes.new.filter(has_location), |  | ||||||
|                              lost: data.nodes.lost.filter(has_location) |  | ||||||
|                             }) |  | ||||||
| 
 |  | ||||||
|         updateView(true) |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (groupOnline) { | ||||||
|  |           groupOnline.clearLayers(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (groupNew) { | ||||||
|  |           groupNew.clearLayers(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (groupLost) { | ||||||
|  |           groupLost.clearLayers(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (groupLines) { | ||||||
|  |           groupLines.clearLayers(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var lines = addLinksToMap(linkDict, linkScale, data.graph.links, router); | ||||||
|  |         groupLines = L.featureGroup(lines).addTo(map); | ||||||
|  | 
 | ||||||
|  |         if (typeof config.fixedCenter === "undefined") { | ||||||
|  |           barycenter = calcBarycenter(data.nodes.all.filter(helper.hasLocation)); | ||||||
|  |         } else { | ||||||
|  |           barycenter = L.circle(L.latLng(new L.LatLng(config.fixedCenter.lat, config.fixedCenter.lng)), config.fixedCenter.radius * 1000); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var nodesOnline = helper.subtract(data.nodes.all.filter(helper.online), data.nodes.new); | ||||||
|  |         var nodesOffline = helper.subtract(data.nodes.all.filter(helper.offline), data.nodes.lost); | ||||||
|  | 
 | ||||||
|  |         var markersOnline = nodesOnline.filter(helper.hasLocation) | ||||||
|  |           .map(mkMarker(nodeDict, function () { | ||||||
|  |             return iconOnline; | ||||||
|  |           }, router)); | ||||||
|  | 
 | ||||||
|  |         var markersOffline = nodesOffline.filter(helper.hasLocation) | ||||||
|  |           .map(mkMarker(nodeDict, function () { | ||||||
|  |             return iconOffline; | ||||||
|  |           }, router)); | ||||||
|  | 
 | ||||||
|  |         var markersNew = data.nodes.new.filter(helper.hasLocation) | ||||||
|  |           .map(mkMarker(nodeDict, function () { | ||||||
|  |             return iconNew; | ||||||
|  |           }, router)); | ||||||
|  | 
 | ||||||
|  |         var markersLost = data.nodes.lost.filter(helper.hasLocation) | ||||||
|  |           .map(mkMarker(nodeDict, function (d) { | ||||||
|  |             if (d.lastseen.isAfter(moment(data.now).subtract(3, "days"))) { | ||||||
|  |               return iconAlert; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (d.lastseen.isAfter(moment(data.now).subtract(14, "days"))) { | ||||||
|  |               return iconLost; | ||||||
|  |             } | ||||||
|  |           }, router)); | ||||||
|  | 
 | ||||||
|  |         groupOffline = L.featureGroup(markersOffline).addTo(map); | ||||||
|  |         groupLost = L.featureGroup(markersLost).addTo(map); | ||||||
|  |         groupOnline = L.featureGroup(markersOnline).addTo(map); | ||||||
|  |         groupNew = L.featureGroup(markersNew).addTo(map); | ||||||
|  | 
 | ||||||
|  |         var rtreeOnlineAll = rbush(9); | ||||||
|  | 
 | ||||||
|  |         rtreeOnlineAll.load(data.nodes.all.filter(helper.online).filter(helper.hasLocation).map(mapRTree)); | ||||||
|  | 
 | ||||||
|  |         clientLayer.setData(rtreeOnlineAll); | ||||||
|  |         labelsLayer.setData({ | ||||||
|  |           online: nodesOnline.filter(helper.hasLocation), | ||||||
|  |           offline: nodesOffline.filter(helper.hasLocation), | ||||||
|  |           new: data.nodes.new.filter(helper.hasLocation), | ||||||
|  |           lost: data.nodes.lost.filter(helper.hasLocation) | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         updateView(true); | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|       self.resetView = function () { |       self.resetView = function () { | ||||||
|         disableTracking() |         disableTracking(); | ||||||
|         highlight = undefined |         highlight = undefined; | ||||||
|         updateView() |         updateView(); | ||||||
|       } |       }; | ||||||
| 
 | 
 | ||||||
|       self.gotoNode = function (d) { |       self.gotoNode = function (d) { | ||||||
|         disableTracking() |         disableTracking(); | ||||||
|         highlight = {type: "node", o: d} |         highlight = {type: "node", o: d}; | ||||||
|         updateView() |         updateView(); | ||||||
|       } |       }; | ||||||
| 
 | 
 | ||||||
|       self.gotoLink = function (d) { |       self.gotoLink = function (d) { | ||||||
|         disableTracking() |         disableTracking(); | ||||||
|         highlight = {type: "link", o: d} |         highlight = {type: "link", o: d}; | ||||||
|         updateView() |         updateView(); | ||||||
|       } |       }; | ||||||
| 
 | 
 | ||||||
|       self.gotoLocation = function () { |       self.gotoLocation = function () { | ||||||
|         //ignore
 |         //ignore
 | ||||||
|       } |       }; | ||||||
| 
 | 
 | ||||||
|       self.destroy = function () { |       self.destroy = function () { | ||||||
|         clearButtons() |         clearButtons(); | ||||||
|         map.remove() |         map.remove(); | ||||||
| 
 | 
 | ||||||
|         if (el.parentNode) |         if (el.parentNode) { | ||||||
|           el.parentNode.removeChild(el) |           el.parentNode.removeChild(el); | ||||||
|         } |         } | ||||||
|  |       }; | ||||||
| 
 | 
 | ||||||
|       self.render = function (d) { |       self.render = function (d) { | ||||||
|         d.appendChild(el) |         d.appendChild(el); | ||||||
|         map.invalidateSize() |         map.invalidateSize(); | ||||||
|       } |       }; | ||||||
| 
 | 
 | ||||||
|       return self |       return self; | ||||||
|     } |     }; | ||||||
| }) |   }); | ||||||
|  |  | ||||||
|  | @ -1,73 +1,78 @@ | ||||||
| define(["leaflet"], | define(["leaflet"], | ||||||
|   function (L) { |   function (L) { | ||||||
|  |     "use strict"; | ||||||
|  | 
 | ||||||
|     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) { | ||||||
|           d.startAngle = (parseInt(d.node.nodeinfo.node_id.substr(10, 2), 16) / 255) * 2 * Math.PI |           d.startAngle = (parseInt(d.node.nodeinfo.node_id.substr(10, 2), 16) / 255) * 2 * Math.PI; | ||||||
|         }) |         }); | ||||||
|         this.redraw() |         this.redraw(); | ||||||
|       }, |       }, | ||||||
|       drawTile: function (canvas, tilePoint) { |       drawTile: function (canvas, tilePoint) { | ||||||
|         function getTileBBox(s, map, tileSize, margin) { |         function getTileBBox(s, map, tileSize, margin) { | ||||||
|           var tl = map.unproject([s.x - margin, s.y - margin]) |           var tl = map.unproject([s.x - margin, s.y - margin]); | ||||||
|           var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]) |           var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]); | ||||||
| 
 | 
 | ||||||
|           return [br.lat, tl.lng, tl.lat, br.lng] |           return [br.lat, tl.lng, tl.lat, br.lng]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!this.data) |         if (!this.data) { | ||||||
|           return |           return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         var tileSize = this.options.tileSize |         var tileSize = this.options.tileSize; | ||||||
|         var s = tilePoint.multiplyBy(tileSize) |         var s = tilePoint.multiplyBy(tileSize); | ||||||
|         var map = this._map |         var map = this._map; | ||||||
| 
 | 
 | ||||||
|         var margin = 50 |         var margin = 50; | ||||||
|         var bbox = getTileBBox(s, map, tileSize, margin) |         var bbox = getTileBBox(s, map, tileSize, margin); | ||||||
| 
 | 
 | ||||||
|         var nodes = this.data.search(bbox) |         var nodes = this.data.search(bbox); | ||||||
| 
 | 
 | ||||||
|         if (nodes.length === 0) |         if (nodes.length === 0) { | ||||||
|           return |           return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         var ctx = canvas.getContext("2d") |         var ctx = canvas.getContext("2d"); | ||||||
| 
 | 
 | ||||||
|         var radius = 3 |         var radius = 3; | ||||||
|         var a = 1.2 |         var a = 1.2; | ||||||
|         var startDistance = 12 |         var startDistance = 12; | ||||||
| 
 | 
 | ||||||
|         ctx.beginPath() |         ctx.beginPath(); | ||||||
|         nodes.forEach(function (d) { |         nodes.forEach(function (d) { | ||||||
|           var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]) |           var p = map.project([d.node.nodeinfo.location.latitude, d.node.nodeinfo.location.longitude]); | ||||||
|           var clients = d.node.statistics.clients |           var clients = d.node.statistics.clients; | ||||||
| 
 | 
 | ||||||
|           if (clients === 0) |           if (clients === 0) { | ||||||
|             return |             return; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|           p.x -= s.x |           p.x -= s.x; | ||||||
|           p.y -= s.y |           p.y -= s.y; | ||||||
| 
 | 
 | ||||||
|           for (var orbit = 0, i = 0; i < clients; orbit++) { |           for (var orbit = 0, i = 0; i < clients; orbit++) { | ||||||
|             var distance = startDistance + orbit * 2 * radius * a |             var distance = startDistance + orbit * 2 * radius * a; | ||||||
|             var n = Math.floor((Math.PI * distance) / (a * radius)) |             var n = Math.floor((Math.PI * distance) / (a * radius)); | ||||||
|             var delta = clients - i |             var delta = clients - i; | ||||||
| 
 | 
 | ||||||
|             for (var j = 0; j < Math.min(delta, n); i++, j++) { |             for (var j = 0; j < Math.min(delta, n); i++, j++) { | ||||||
|               var angle = 2 * Math.PI / n * j |               var angle = 2 * Math.PI / n * j; | ||||||
|               var x = p.x + distance * Math.cos(angle + d.startAngle) |               var x = p.x + distance * Math.cos(angle + d.startAngle); | ||||||
|               var y = p.y + distance * Math.sin(angle + d.startAngle) |               var y = p.y + distance * Math.sin(angle + d.startAngle); | ||||||
| 
 | 
 | ||||||
|               ctx.moveTo(x, y) |               ctx.moveTo(x, y); | ||||||
|               ctx.arc(x, y, radius, 0, 2 * Math.PI) |               ctx.arc(x, y, radius, 0, 2 * Math.PI); | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         }) |         }); | ||||||
| 
 | 
 | ||||||
|         ctx.fillStyle = "rgba(220, 0, 103, 0.7)" |         ctx.fillStyle = "rgba(220, 0, 103, 0.7)"; | ||||||
|         ctx.fill() |         ctx.fill(); | ||||||
|       } |       } | ||||||
|     }) |     }); | ||||||
| }) |   }); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| define(["leaflet", "rbush"], | define(["leaflet", "rbush"], | ||||||
|   function (L, rbush) { |   function (L, rbush) { | ||||||
|  |     "use strict"; | ||||||
|  | 
 | ||||||
|     var labelLocations = [["left", "middle", 0 / 8], |     var labelLocations = [["left", "middle", 0 / 8], | ||||||
|       ["center", "top", 6 / 8], |       ["center", "top", 6 / 8], | ||||||
|       ["right", "middle", 4 / 8], |       ["right", "middle", 4 / 8], | ||||||
|  | @ -7,30 +9,31 @@ define(["leaflet", "rbush"], | ||||||
|       ["left", "ideographic", 1 / 8], |       ["left", "ideographic", 1 / 8], | ||||||
|       ["right", "top", 5 / 8], |       ["right", "top", 5 / 8], | ||||||
|       ["center", "ideographic", 2 / 8], |       ["center", "ideographic", 2 / 8], | ||||||
|                           ["right",  "ideographic", 3 / 8]] |       ["right", "ideographic", 3 / 8]]; | ||||||
| 
 | 
 | ||||||
|     var fontFamily = "Roboto" |     var fontFamily = "Roboto"; | ||||||
|     var nodeRadius = 4 |     var nodeRadius = 4; | ||||||
| 
 | 
 | ||||||
|     var ctx = document.createElement("canvas").getContext("2d") |     var ctx = document.createElement("canvas").getContext("2d"); | ||||||
| 
 | 
 | ||||||
|     function measureText(font, text) { |     function measureText(font, text) { | ||||||
|       ctx.font = font |       ctx.font = font; | ||||||
|       return ctx.measureText(text) |       return ctx.measureText(text); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function mapRTree(d) { |     function mapRTree(d) { | ||||||
|       var o = [d.position.lat, d.position.lng, d.position.lat, d.position.lng] |       var o = [d.position.lat, d.position.lng, d.position.lat, d.position.lng]; | ||||||
| 
 | 
 | ||||||
|       o.label = d |       o.label = d; | ||||||
| 
 | 
 | ||||||
|       return o |       return o; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function prepareLabel(fillStyle, fontSize, offset, stroke, minZoom) { |     function prepareLabel(fillStyle, fontSize, offset, stroke, minZoom) { | ||||||
|       return function (d) { |       return function (d) { | ||||||
|         var font = fontSize + "px " + fontFamily |         var font = fontSize + "px " + fontFamily; | ||||||
|         return { position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude), |         return { | ||||||
|  |           position: L.latLng(d.nodeinfo.location.latitude, d.nodeinfo.location.longitude), | ||||||
|           label: d.nodeinfo.hostname, |           label: d.nodeinfo.hostname, | ||||||
|           offset: offset, |           offset: offset, | ||||||
|           fillStyle: fillStyle, |           fillStyle: fillStyle, | ||||||
|  | @ -39,50 +42,54 @@ define(["leaflet", "rbush"], | ||||||
|           stroke: stroke, |           stroke: stroke, | ||||||
|           minZoom: minZoom, |           minZoom: minZoom, | ||||||
|           width: measureText(font, d.nodeinfo.hostname).width |           width: measureText(font, d.nodeinfo.hostname).width | ||||||
|                } |         }; | ||||||
|       } |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function calcOffset(offset, loc) { |     function calcOffset(offset, loc) { | ||||||
|       return [offset * Math.cos(loc[2] * 2 * Math.PI), |       return [offset * Math.cos(loc[2] * 2 * Math.PI), | ||||||
|               -offset * Math.sin(loc[2] * 2 * Math.PI)] |         -offset * Math.sin(loc[2] * 2 * Math.PI)]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) { |     function labelRect(p, offset, anchor, label, minZoom, maxZoom, z) { | ||||||
|       var margin = 1 + 1.41 * (1 - (z - minZoom) / (maxZoom - minZoom)) |       var margin = 1 + 1.41 * (1 - (z - minZoom) / (maxZoom - minZoom)); | ||||||
| 
 | 
 | ||||||
|       var width = label.width * margin |       var width = label.width * margin; | ||||||
|       var height = label.height * margin |       var height = label.height * margin; | ||||||
| 
 | 
 | ||||||
|       var dx = { left: 0, |       var dx = { | ||||||
|  |         left: 0, | ||||||
|         right: -width, |         right: -width, | ||||||
|         center: -width / 2 |         center: -width / 2 | ||||||
|                } |       }; | ||||||
| 
 | 
 | ||||||
|       var dy = { top: 0, |       var dy = { | ||||||
|  |         top: 0, | ||||||
|         ideographic: -height, |         ideographic: -height, | ||||||
|         middle: -height / 2 |         middle: -height / 2 | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       var x = p.x + offset[0] + dx[anchor[0]]; | ||||||
|  |       var y = p.y + offset[1] + dy[anchor[1]]; | ||||||
|  | 
 | ||||||
|  |       return [x, y, x + width, y + height]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|       var x = p.x + offset[0] + dx[anchor[0]] |     return L.TileLayer.Canvas.extend({ | ||||||
|       var y = p.y + offset[1] + dy[anchor[1]] |  | ||||||
| 
 |  | ||||||
|       return [x, y, x + width, y + height] |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     var c = L.TileLayer.Canvas.extend({ |  | ||||||
|       onAdd: function (map) { |       onAdd: function (map) { | ||||||
|         L.TileLayer.Canvas.prototype.onAdd.call(this, map) |         L.TileLayer.Canvas.prototype.onAdd.call(this, map); | ||||||
|         if (this.data) |         if (this.data) { | ||||||
|           this.prepareLabels() |           this.prepareLabels(); | ||||||
|  |         } | ||||||
|       }, |       }, | ||||||
|       setData: function (d) { |       setData: function (d) { | ||||||
|         this.data = d |         this.data = d; | ||||||
|         if (this._map) |         if (this._map) { | ||||||
|           this.prepareLabels() |           this.prepareLabels(); | ||||||
|  |         } | ||||||
|       }, |       }, | ||||||
|       prepareLabels: function () { |       prepareLabels: function () { | ||||||
|         var d = this.data |         var d = this.data; | ||||||
| 
 | 
 | ||||||
|         // label:
 |         // label:
 | ||||||
|         // - position (WGS84 coords)
 |         // - position (WGS84 coords)
 | ||||||
|  | @ -92,137 +99,142 @@ define(["leaflet", "rbush"], | ||||||
|         // - label (string)
 |         // - label (string)
 | ||||||
|         // - color (string)
 |         // - color (string)
 | ||||||
| 
 | 
 | ||||||
|         var labelsOnline = d.online.map(prepareLabel("rgba(0, 0, 0, 0.9)", 10, 8, true, 13)) |         var labelsOnline = d.online.map(prepareLabel("rgba(0, 0, 0, 0.9)", 10, 8, true, 13)); | ||||||
|         var labelsOffline = d.offline.map(prepareLabel("rgba(212, 62, 42, 0.9)", 9, 5, false, 16)) |         var labelsOffline = d.offline.map(prepareLabel("rgba(212, 62, 42, 0.9)", 9, 5, false, 16)); | ||||||
|         var labelsNew = d.new.map(prepareLabel("rgba(48, 99, 20, 0.9)", 11, 8, true, 0)) |         var labelsNew = d.new.map(prepareLabel("rgba(48, 99, 20, 0.9)", 11, 8, true, 0)); | ||||||
|         var labelsLost = d.lost.map(prepareLabel("rgba(212, 62, 42, 0.9)", 11, 8, true, 0)) |         var labelsLost = d.lost.map(prepareLabel("rgba(212, 62, 42, 0.9)", 11, 8, true, 0)); | ||||||
| 
 | 
 | ||||||
|         var labels = [] |         var labels = [] | ||||||
|           .concat(labelsNew) |           .concat(labelsNew) | ||||||
|           .concat(labelsLost) |           .concat(labelsLost) | ||||||
|           .concat(labelsOnline) |           .concat(labelsOnline) | ||||||
|                      .concat(labelsOffline) |           .concat(labelsOffline); | ||||||
| 
 | 
 | ||||||
|         var minZoom = this.options.minZoom |         var minZoom = this.options.minZoom; | ||||||
|         var maxZoom = this.options.maxZoom |         var maxZoom = this.options.maxZoom; | ||||||
| 
 | 
 | ||||||
|         var trees = [] |         var trees = []; | ||||||
| 
 | 
 | ||||||
|         var map = this._map |         var map = this._map; | ||||||
| 
 | 
 | ||||||
|         function nodeToRect(z) { |         function nodeToRect(z) { | ||||||
|           return function (d) { |           return function (d) { | ||||||
|             var p = map.project(d.position, z) |             var p = map.project(d.position, z); | ||||||
|             return [p.x - nodeRadius, p.y - nodeRadius, |             return [p.x - nodeRadius, p.y - nodeRadius, | ||||||
|                     p.x + nodeRadius, p.y + nodeRadius] |               p.x + nodeRadius, p.y + nodeRadius]; | ||||||
|           } |           }; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for (var z = minZoom; z <= maxZoom; z++) { |         for (var z = minZoom; z <= maxZoom; z++) { | ||||||
|           trees[z] = rbush(9) |           trees[z] = rbush(9); | ||||||
|           trees[z].load(labels.map(nodeToRect(z))) |           trees[z].load(labels.map(nodeToRect(z))); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         labels = labels.map(function (d) { |         labels = labels.map(function (d) { | ||||||
|           var best = labelLocations.map(function (loc) { |           var best = labelLocations.map(function (loc) { | ||||||
|             var offset = calcOffset(d.offset, loc) |             var offset = calcOffset(d.offset, loc); | ||||||
|             var z |             var z; | ||||||
| 
 | 
 | ||||||
|             for (z = maxZoom; z >= d.minZoom; z--) { |             for (z = maxZoom; z >= d.minZoom; z--) { | ||||||
|               var p = map.project(d.position, z) |               var p = map.project(d.position, z); | ||||||
|               var rect = labelRect(p, offset, loc, d, minZoom, maxZoom, z) |               var rect = labelRect(p, offset, loc, d, minZoom, maxZoom, z); | ||||||
|               var candidates = trees[z].search(rect) |               var candidates = trees[z].search(rect); | ||||||
| 
 | 
 | ||||||
|               if (candidates.length > 0) |               if (candidates.length > 0) { | ||||||
|                 break |                 break; | ||||||
|  |               } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return {loc: loc, z: z + 1} |             return {loc: loc, z: z + 1}; | ||||||
|           }).filter(function (d) { |           }).filter(function (d) { | ||||||
|             return d.z <= maxZoom |             return d.z <= maxZoom; | ||||||
|           }).sort(function (a, b) { |           }).sort(function (a, b) { | ||||||
|             return a.z - b.z |             return a.z - b.z; | ||||||
|           })[0] |           })[0]; | ||||||
| 
 | 
 | ||||||
|           if (best !== undefined) { |           if (best !== undefined) { | ||||||
|             d.offset = calcOffset(d.offset, best.loc) |             d.offset = calcOffset(d.offset, best.loc); | ||||||
|             d.minZoom = best.z |             d.minZoom = best.z; | ||||||
|             d.anchor = best.loc |             d.anchor = best.loc; | ||||||
| 
 | 
 | ||||||
|             for (var z = maxZoom; z >= best.z; z--) { |             for (var z = maxZoom; z >= best.z; z--) { | ||||||
|               var p = map.project(d.position, z) |               var p = map.project(d.position, z); | ||||||
|               var rect = labelRect(p, d.offset, best.loc, d, minZoom, maxZoom, z) |               var rect = labelRect(p, d.offset, best.loc, d, minZoom, maxZoom, z); | ||||||
|               trees[z].insert(rect) |               trees[z].insert(rect); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return d |             return d; | ||||||
|           } else |           } else { | ||||||
|             return undefined |             return undefined; | ||||||
|         }).filter(function (d) { return d !== undefined }) |           } | ||||||
|  |         }).filter(function (d) { | ||||||
|  |           return d !== undefined; | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         this.margin = 16 |         this.margin = 16; | ||||||
| 
 | 
 | ||||||
|         if (labels.length > 0) |         if (labels.length > 0) { | ||||||
|           this.margin += labels.map(function (d) { |           this.margin += labels.map(function (d) { | ||||||
|             return d.width |             return d.width; | ||||||
|           }).sort().reverse()[0] |           }).sort().reverse()[0]; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         this.labels = rbush(9) |         this.labels = rbush(9); | ||||||
|         this.labels.load(labels.map(mapRTree)) |         this.labels.load(labels.map(mapRTree)); | ||||||
| 
 | 
 | ||||||
|         this.redraw() |         this.redraw(); | ||||||
|       }, |       }, | ||||||
|       drawTile: function (canvas, tilePoint, zoom) { |       drawTile: function (canvas, tilePoint, zoom) { | ||||||
|         function getTileBBox(s, map, tileSize, margin) { |         function getTileBBox(s, map, tileSize, margin) { | ||||||
|           var tl = map.unproject([s.x - margin, s.y - margin]) |           var tl = map.unproject([s.x - margin, s.y - margin]); | ||||||
|           var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]) |           var br = map.unproject([s.x + margin + tileSize, s.y + margin + tileSize]); | ||||||
| 
 | 
 | ||||||
|           return [br.lat, tl.lng, tl.lat, br.lng] |           return [br.lat, tl.lng, tl.lat, br.lng]; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!this.labels) |         if (!this.labels) { | ||||||
|           return |           return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         var tileSize = this.options.tileSize |         var tileSize = this.options.tileSize; | ||||||
|         var s = tilePoint.multiplyBy(tileSize) |         var s = tilePoint.multiplyBy(tileSize); | ||||||
|         var map = this._map |         var map = this._map; | ||||||
| 
 | 
 | ||||||
|         function projectNodes(d) { |         function projectNodes(d) { | ||||||
|           var p = map.project(d.label.position) |           var p = map.project(d.label.position); | ||||||
| 
 | 
 | ||||||
|           p.x -= s.x |           p.x -= s.x; | ||||||
|           p.y -= s.y |           p.y -= s.y; | ||||||
| 
 | 
 | ||||||
|           return {p: p, label: d.label} |           return {p: p, label: d.label}; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var bbox = getTileBBox(s, map, tileSize, this.margin) |         var bbox = getTileBBox(s, map, tileSize, this.margin); | ||||||
| 
 | 
 | ||||||
|         var labels = this.labels.search(bbox).map(projectNodes) |         var labels = this.labels.search(bbox).map(projectNodes); | ||||||
| 
 | 
 | ||||||
|         var ctx = canvas.getContext("2d") |         var ctx = canvas.getContext("2d"); | ||||||
| 
 | 
 | ||||||
|         ctx.lineWidth = 5 |         ctx.lineWidth = 5; | ||||||
|         ctx.strokeStyle = "rgba(255, 255, 255, 0.8)" |         ctx.strokeStyle = "rgba(255, 255, 255, 0.8)"; | ||||||
|         ctx.miterLimit = 2 |         ctx.miterLimit = 2; | ||||||
| 
 | 
 | ||||||
|         function drawLabel(d) { |         function drawLabel(d) { | ||||||
|           ctx.font = d.label.font |           ctx.font = d.label.font; | ||||||
|           ctx.textAlign = d.label.anchor[0] |           ctx.textAlign = d.label.anchor[0]; | ||||||
|           ctx.textBaseline = d.label.anchor[1] |           ctx.textBaseline = d.label.anchor[1]; | ||||||
|           ctx.fillStyle = d.label.fillStyle |           ctx.fillStyle = d.label.fillStyle; | ||||||
| 
 | 
 | ||||||
|           if (d.label.stroke) |           if (d.label.stroke) { | ||||||
|             ctx.strokeText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]) |             ctx.strokeText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]); | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|           ctx.fillText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]) |           ctx.fillText(d.label.label, d.p.x + d.label.offset[0], d.p.y + d.label.offset[1]); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         labels.filter(function (d) { |         labels.filter(function (d) { | ||||||
|           return zoom >= d.label.minZoom |           return zoom >= d.label.minZoom; | ||||||
|         }).forEach(drawLabel) |         }).forEach(drawLabel); | ||||||
|       } |       } | ||||||
|     }) |     }); | ||||||
| 
 |   }); | ||||||
|     return c |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  | @ -1,55 +1,63 @@ | ||||||
| define(function () { | define(["helper"], function (helper) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (config) { |   return function (config) { | ||||||
|     var self = this |     var self = this; | ||||||
|     var stats, timestamp |     var stats, timestamp; | ||||||
| 
 | 
 | ||||||
|     self.setData = function (d) { |     self.setData = function (d) { | ||||||
|       var totalNodes = sum(d.nodes.all.map(one)) |       var totalNodes = helper.sum(d.nodes.all.map(helper.one)); | ||||||
|       var totalOnlineNodes = sum(d.nodes.all.filter(online).map(one)) |       var totalOnlineNodes = helper.sum(d.nodes.all.filter(helper.online).map(helper.one)); | ||||||
|       var totalOfflineNodes = sum(d.nodes.all.filter(function (node) {return !node.flags.online}).map(one)) |       var totalOfflineNodes = helper.sum(d.nodes.all.filter(function (node) { | ||||||
|       var totalNewNodes = sum(d.nodes.new.map(one)) |         return !node.flags.online; | ||||||
|       var totalLostNodes = sum(d.nodes.lost.map(one)) |       }).map(helper.one)); | ||||||
|       var totalClients = sum(d.nodes.all.filter(online).map( function (d) { |       var totalNewNodes = helper.sum(d.nodes.new.map(helper.one)); | ||||||
|         return d.statistics.clients ? d.statistics.clients : 0 |       var totalLostNodes = helper.sum(d.nodes.lost.map(helper.one)); | ||||||
|       })) |       var totalClients = helper.sum(d.nodes.all.filter(helper.online).map(function (d) { | ||||||
|       var totalGateways = sum(Array.from(new Set(d.nodes.all.filter(online).map( function(d) { |         return d.statistics.clients ? d.statistics.clients : 0; | ||||||
|         return ("gateway" in d.statistics && d.statistics.gateway.id) ? d.statistics.gateway.id : d.statistics.gateway |       })); | ||||||
|  |       var totalGateways = helper.sum(Array.from(new Set(d.nodes.all.filter(helper.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) { |       }).concat(d.nodes.all.filter(function (d) { | ||||||
|         return d.flags.gateway |         return d.flags.gateway; | ||||||
|       })))).map(function (d) { |       })))).map(function (d) { | ||||||
|         return (typeof d === "string") ? 1 : 0 |         return (typeof d === "string") ? 1 : 0; | ||||||
|       })) |       })); | ||||||
| 
 | 
 | ||||||
|       var nodetext = [{count: totalOnlineNodes, label: "online"}, |       var nodetext = [{count: totalOnlineNodes, label: "online"}, | ||||||
|         {count: totalOfflineNodes, label: "offline"}, |         {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) { | ||||||
|                       .map( function (d) { return [d.count, d.label].join(" ") } ) |         return d.count > 0; | ||||||
|                       .join(", ") |       }) | ||||||
|  |         .map(function (d) { | ||||||
|  |           return [d.count, d.label].join(" "); | ||||||
|  |         }) | ||||||
|  |         .join(", "); | ||||||
| 
 | 
 | ||||||
|       stats.textContent = totalNodes + " Knoten " + |       stats.textContent = totalNodes + " Knoten " + | ||||||
|         "(" + nodetext + "), " + |         "(" + nodetext + "), " + | ||||||
|         totalClients + " Client" + ( totalClients === 1 ? ", " : "s, " ) + |         totalClients + " Client" + ( totalClients === 1 ? ", " : "s, " ) + | ||||||
|                           totalGateways + " Gateways" |         totalGateways + " Gateways"; | ||||||
| 
 | 
 | ||||||
|       timestamp.textContent = "Diese Daten sind von " + d.timestamp.format("LLLL") + "." |       timestamp.textContent = "Diese Daten sind von " + d.timestamp.format("LLLL") + "."; | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.render = function (el) { |     self.render = function (el) { | ||||||
|       var h2 = document.createElement("h2") |       var h2 = document.createElement("h2"); | ||||||
|       h2.textContent = config.siteName |       h2.textContent = config.siteName; | ||||||
|       el.appendChild(h2) |       el.appendChild(h2); | ||||||
| 
 | 
 | ||||||
|       var p = document.createElement("p") |       var p = document.createElement("p"); | ||||||
|       el.appendChild(p) |       el.appendChild(p); | ||||||
|       stats = document.createTextNode("") |       stats = document.createTextNode(""); | ||||||
|       p.appendChild(stats) |       p.appendChild(stats); | ||||||
|       p.appendChild(document.createElement("br")) |       p.appendChild(document.createElement("br")); | ||||||
|       timestamp = document.createTextNode("") |       timestamp = document.createTextNode(""); | ||||||
|       p.appendChild(timestamp) |       p.appendChild(timestamp); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     return self |     return self; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
							
								
								
									
										126
									
								
								lib/nodelist.js
									
										
									
									
									
								
							
							
						
						
									
										126
									
								
								lib/nodelist.js
									
										
									
									
									
								
							|  | @ -1,97 +1,109 @@ | ||||||
| define(["sorttable", "virtual-dom", "numeral"], function (SortTable, V, numeral) { | define(["sorttable", "virtual-dom", "helper"], function (SortTable, V, helper) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   function getUptime(now, d) { |   function getUptime(now, d) { | ||||||
|     if (d.flags.online && "uptime" in d.statistics) |     if (d.flags.online && "uptime" in d.statistics) { | ||||||
|       return Math.round(d.statistics.uptime) |       return Math.round(d.statistics.uptime); | ||||||
|     else if (!d.flags.online && "lastseen" in d) |     } else if (!d.flags.online && "lastseen" in d) { | ||||||
|       return Math.round(-(now.unix() - d.lastseen.unix())) |       return Math.round(-(now.unix() - d.lastseen.unix())); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   function showUptime(uptime) { |   function showUptime(uptime) { | ||||||
|     var s = "" |     var s = ""; | ||||||
|     uptime /= 3600 |     uptime /= 3600; | ||||||
| 
 | 
 | ||||||
|     if (uptime !== undefined) |     if (uptime !== undefined) { | ||||||
|       if (Math.abs(uptime) >= 24) |       if (Math.abs(uptime) >= 24) { | ||||||
|         s = Math.round(uptime / 24) + "d" |         s = Math.round(uptime / 24) + "d"; | ||||||
|       else |       } else { | ||||||
|         s = Math.round(uptime) + "h" |         s = Math.round(uptime) + "h"; | ||||||
| 
 |       } | ||||||
|     return s |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|   var headings = [{ name: "Knoten", |     return s; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var headings = [{ | ||||||
|  |     name: "Knoten", | ||||||
|     sort: function (a, b) { |     sort: function (a, b) { | ||||||
|                       var aname = typeof a.nodeinfo.hostname === "string" ? a.nodeinfo.hostname : a.nodeinfo.node_id |       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 |       var bname = typeof b.nodeinfo.hostname === "string" ? b.nodeinfo.hostname : b.nodeinfo.node_id; | ||||||
|                       if (typeof aname === "string" && typeof bname === "string") |       if (typeof aname === "string" && typeof bname === "string") { | ||||||
|                         return aname.localeCompare(bname) |         return aname.localeCompare(bname); | ||||||
|                       return typeof aname === "string" ? 1 : typeof bname === "string" ? -1 : 0 |       } | ||||||
|  |       return typeof aname === "string" ? 1 : typeof bname === "string" ? -1 : 0; | ||||||
|     }, |     }, | ||||||
|     reverse: false |     reverse: false | ||||||
|   }, |   }, | ||||||
|                   { name: "Uptime", |     { | ||||||
|  |       name: "Uptime", | ||||||
|       sort: function (a, b) { |       sort: function (a, b) { | ||||||
|                       return a.uptime - b.uptime |         return a.uptime - b.uptime; | ||||||
|       }, |       }, | ||||||
|       reverse: true |       reverse: true | ||||||
|     }, |     }, | ||||||
|                   { name: "#Links", |     { | ||||||
|  |       name: "#Links", | ||||||
|       sort: function (a, b) { |       sort: function (a, b) { | ||||||
|                       return a.meshlinks - b.meshlinks |         return a.meshlinks - b.meshlinks; | ||||||
|       }, |       }, | ||||||
|       reverse: true |       reverse: true | ||||||
|     }, |     }, | ||||||
|                   { name: "Clients", |     { | ||||||
|  |       name: "Clients", | ||||||
|       sort: function (a, b) { |       sort: function (a, b) { | ||||||
|         return ("clients" in a.statistics ? a.statistics.clients : -1) - |         return ("clients" in a.statistics ? a.statistics.clients : -1) - | ||||||
|                              ("clients" in b.statistics ? b.statistics.clients : -1) |           ("clients" in b.statistics ? b.statistics.clients : -1); | ||||||
|       }, |       }, | ||||||
|       reverse: true |       reverse: true | ||||||
|                   }] |     }]; | ||||||
| 
 | 
 | ||||||
|   return function (router) { |   return function (router) { | ||||||
|     function renderRow(d) { |     function renderRow(d) { | ||||||
|       var td1Content = [] |       var td1Content = []; | ||||||
|       var aClass = ["hostname", d.flags.online ? "online" : "offline"] |       var aClass = ["hostname", d.flags.online ? "online" : "offline"]; | ||||||
| 
 | 
 | ||||||
|       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: "#!n:" + d.nodeinfo.node_id |         href: "#!n:" + d.nodeinfo.node_id | ||||||
|                                }, d.nodeinfo.hostname)) |       }, d.nodeinfo.hostname)); | ||||||
| 
 | 
 | ||||||
|       if (has_location(d)) |       if (helper.hasLocation(d)) { | ||||||
|         td1Content.push(V.h("span", {className: "icon ion-location"})) |         td1Content.push(V.h("span", {className: "icon ion-location"})); | ||||||
| 
 |  | ||||||
|       var td1 = V.h("td", td1Content) |  | ||||||
|       var td2 = V.h("td", showUptime(d.uptime)) |  | ||||||
|       var td3 = V.h("td", d.meshlinks.toString()) |  | ||||||
|       var td4 = V.h("td", numeral("clients" in d.statistics ? d.statistics.clients : "").format("0,0")) |  | ||||||
| 
 |  | ||||||
|       return V.h("tr", [td1, td2, td3, td4]) |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|     var table = new SortTable(headings, 0, renderRow) |       var td1 = V.h("td", td1Content); | ||||||
|  |       var td2 = V.h("td", showUptime(d.uptime)); | ||||||
|  |       var td3 = V.h("td", d.meshlinks.toString()); | ||||||
|  |       var td4 = V.h("td", ("clients" in d.statistics ? d.statistics.clients : 0).toFixed(0)); | ||||||
|  | 
 | ||||||
|  |       return V.h("tr", [td1, td2, td3, td4]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var table = new SortTable(headings, 0, renderRow); | ||||||
| 
 | 
 | ||||||
|     this.render = function (d) { |     this.render = function (d) { | ||||||
|       var el = document.createElement("div") |       var el = document.createElement("div"); | ||||||
|       d.appendChild(el) |       d.appendChild(el); | ||||||
| 
 | 
 | ||||||
|       var h2 = document.createElement("h2") |       var h2 = document.createElement("h2"); | ||||||
|       h2.textContent = "Alle Knoten" |       h2.textContent = "Alle Knoten"; | ||||||
|       el.appendChild(h2) |       el.appendChild(h2); | ||||||
| 
 | 
 | ||||||
|       el.appendChild(table.el) |       el.appendChild(table.el); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     this.setData = function (d) { |     this.setData = function (d) { | ||||||
|       var data = d.nodes.all.map(function (e) { |       var data = d.nodes.all.map(function (e) { | ||||||
|         var n = Object.create(e) |         var n = Object.create(e); | ||||||
|         n.uptime = getUptime(d.now, e) || 0 |         n.uptime = getUptime(d.now, e) || 0; | ||||||
|         n.meshlinks = e.meshlinks || 0 |         n.meshlinks = e.meshlinks || 0; | ||||||
|         return n |         return n; | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       table.setData(data) |       table.setData(data); | ||||||
|     } |     }; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,236 +1,284 @@ | ||||||
| define(["chroma-js", "virtual-dom", "numeral-intl", "filters/genericnode", "vercomp" ], | define(["chroma-js", "virtual-dom", "filters/genericnode", "helper"], | ||||||
|   function (Chroma, V, numeral, Filter, vercomp) { |   function (Chroma, V, Filter, helper) { | ||||||
|  |     "use strict"; | ||||||
| 
 | 
 | ||||||
|     return function (config, filterManager) { |     return function (config, filterManager) { | ||||||
|     var self = this |       var self = this; | ||||||
|     var scale = Chroma.scale("YlGnBu").mode("lab") |       var scale = Chroma.scale("YlGnBu").mode("lab"); | ||||||
| 
 | 
 | ||||||
|     var statusTable = document.createElement("table") |       var statusTable = document.createElement("table"); | ||||||
|     statusTable.classList.add("proportion") |       statusTable.classList.add("proportion"); | ||||||
| 
 | 
 | ||||||
|     var fwTable = document.createElement("table") |       var fwTable = document.createElement("table"); | ||||||
|     fwTable.classList.add("proportion") |       fwTable.classList.add("proportion"); | ||||||
| 
 | 
 | ||||||
|     var hwTable = document.createElement("table") |       var hwTable = document.createElement("table"); | ||||||
|     hwTable.classList.add("proportion") |       hwTable.classList.add("proportion"); | ||||||
| 
 | 
 | ||||||
|     var geoTable = document.createElement("table") |       var geoTable = document.createElement("table"); | ||||||
|     geoTable.classList.add("proportion") |       geoTable.classList.add("proportion"); | ||||||
| 
 | 
 | ||||||
|     var autoTable = document.createElement("table") |       var autoTable = document.createElement("table"); | ||||||
|     autoTable.classList.add("proportion") |       autoTable.classList.add("proportion"); | ||||||
| 
 | 
 | ||||||
|     var uplinkTable = document.createElement("table") |       var uplinkTable = document.createElement("table"); | ||||||
|     uplinkTable.classList.add("proportion") |       uplinkTable.classList.add("proportion"); | ||||||
| 
 | 
 | ||||||
|     var  gwNodesTable = document.createElement("table") |       var gwNodesTable = document.createElement("table"); | ||||||
|     gwNodesTable.classList.add("proportion") |       gwNodesTable.classList.add("proportion"); | ||||||
| 
 | 
 | ||||||
|     var gwClientsTable = document.createElement("table") |       var gwClientsTable = document.createElement("table"); | ||||||
|     gwClientsTable.classList.add("proportion") |       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) { | ||||||
|       return showStat(o) |         return helper.showStat(o); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function count(nodes, key, f) { |       function count(nodes, key, f) { | ||||||
|       var dict = {} |         var dict = {}; | ||||||
| 
 | 
 | ||||||
|         nodes.forEach(function (d) { |         nodes.forEach(function (d) { | ||||||
|         var v = dictGet(d, key.slice(0)) |           var v = helper.dictGet(d, key.slice(0)); | ||||||
| 
 | 
 | ||||||
|         if (f !== undefined) |           if (f !== undefined) { | ||||||
|           v = f(v) |             v = f(v); | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         if (v === null) |           if (v === null) { | ||||||
|           return |             return; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         dict[v] = 1 + (v in dict ? dict[v] : 0) |           dict[v] = 1 + (v in dict ? dict[v] : 0); | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|       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) { |       function countClients(nodes, key, f) { | ||||||
|       var dict = {} |         var dict = {}; | ||||||
| 
 | 
 | ||||||
|         nodes.forEach(function (d) { |         nodes.forEach(function (d) { | ||||||
|         var v = dictGet(d, key.slice(0)) |           var v = helper.dictGet(d, key.slice(0)); | ||||||
| 
 | 
 | ||||||
|         if (f !== undefined) |           if (f !== undefined) { | ||||||
|           v = f(v) |             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] }) |  | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|  |           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); | ||||||
| 
 | 
 | ||||||
|         return false |           return false; | ||||||
|       } |         }; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       function fillTable(name, table, data) { |       function fillTable(name, table, data) { | ||||||
|       if (!table.last) |         if (!table.last) { | ||||||
|         table.last = V.h("table") |           table.last = V.h("table"); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|       var max = 0 |         var max = 0; | ||||||
|         data.forEach(function (d) { |         data.forEach(function (d) { | ||||||
|         if (d[1] > max) |           if (d[1] > max) { | ||||||
|           max = d[1] |             max = d[1]; | ||||||
|       }) |           } | ||||||
|  |         }); | ||||||
| 
 | 
 | ||||||
|         var items = data.map(function (d) { |         var items = data.map(function (d) { | ||||||
|         var v = d[1] / max |           var v = d[1] / max; | ||||||
|         var c1 = Chroma.contrast(scale(v), "white") |           var c1 = Chroma.contrast(scale(v), "white"); | ||||||
|         var c2 = Chroma.contrast(scale(v), "black") |           var c2 = Chroma.contrast(scale(v), "black"); | ||||||
| 
 | 
 | ||||||
|         var filter = new Filter(name, d[2], d[0], d[3]) |           var filter = new Filter(name, d[2], d[0], d[3]); | ||||||
| 
 | 
 | ||||||
|         var a = V.h("a", { href: "#", onclick: addFilter(filter) }, d[0]) |           var a = V.h("a", {href: "#", onclick: addFilter(filter)}, d[0]); | ||||||
| 
 | 
 | ||||||
|         var th = V.h("th", a) |           var th = V.h("th", a); | ||||||
|         var td = V.h("td", V.h("span", {style: { |           var td = V.h("td", V.h("span", { | ||||||
|  |             style: { | ||||||
|               width: Math.round(v * 100) + "%", |               width: Math.round(v * 100) + "%", | ||||||
|               backgroundColor: scale(v).hex(), |               backgroundColor: scale(v).hex(), | ||||||
|               color: c1 > c2 ? "white" : "black" |               color: c1 > c2 ? "white" : "black" | ||||||
|                                        }}, numeral(d[1]).format("0,0"))) |             } | ||||||
|  |           }, d[1].toFixed(0))); | ||||||
| 
 | 
 | ||||||
|         return V.h("tr", [th, td]) |           return V.h("tr", [th, td]); | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|       var tableNew = V.h("table", items) |         var tableNew = V.h("table", items); | ||||||
|       table = V.patch(table, V.diff(table.last, tableNew)) |         table = V.patch(table, V.diff(table.last, tableNew)); | ||||||
|       table.last = tableNew |         table.last = tableNew; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       self.setData = function (data) { |       self.setData = function (data) { | ||||||
|       var onlineNodes = data.nodes.all.filter(online) |         var onlineNodes = data.nodes.all.filter(helper.online); | ||||||
|       var nodes = onlineNodes.concat(data.nodes.lost) |         var nodes = onlineNodes.concat(data.nodes.lost); | ||||||
|       var nodeDict = {} |         var nodeDict = {}; | ||||||
| 
 | 
 | ||||||
|         data.nodes.all.forEach(function (d) { |         data.nodes.all.forEach(function (d) { | ||||||
|         nodeDict[d.nodeinfo.node_id] = d |           nodeDict[d.nodeinfo.node_id] = d; | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|         var statusDict = count(nodes, ["flags", "online"], function (d) { |         var statusDict = count(nodes, ["flags", "online"], function (d) { | ||||||
|         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"], function (d) { |         var hwDict = count(nodes, ["nodeinfo", "hardware", "model"], function (d) { | ||||||
|           if (d) { |           if (d) { | ||||||
|           d = d.replace(/\(r\)|\(tm\)/gi, "").replace(/AMD |Intel |TP-Link | CPU| Processor/g, "") |             d = d.replace(/\(r\)|\(tm\)/gi, "").replace(/AMD |Intel |TP-Link | CPU| Processor/g, ""); | ||||||
|           if (d.indexOf("@") > 0) d = d.substring(0, d.indexOf("@")) |             if (d.indexOf("@") > 0) { | ||||||
|  |               d = d.substring(0, d.indexOf("@")); | ||||||
|             } |             } | ||||||
|         return d |           } | ||||||
|       }) |           return d; | ||||||
|  |         }); | ||||||
|         var geoDict = count(nodes, ["nodeinfo", "location"], function (d) { |         var geoDict = count(nodes, ["nodeinfo", "location"], function (d) { | ||||||
|         return d && d.longitude && d.latitude ? "ja" : "nein" |           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; | ||||||
|         else if (d.enabled) |           } else if (d.enabled) { | ||||||
|           return d.branch |             return d.branch; | ||||||
|         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 gwNodesDict = count(onlineNodes, ["statistics", "gateway"], function (d) { |         var gwNodesDict = count(onlineNodes, ["statistics", "gateway"], function (d) { | ||||||
|         if (d === null) |           if (d === null) { | ||||||
|           return null |             return null; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         if (d.node) |           if (d.node) { | ||||||
|           return d.node.nodeinfo.hostname |             return d.node.nodeinfo.hostname; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         if (d.id) |           if (d.id) { | ||||||
|           return d.id |             return d.id; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         return d |           return d; | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|         var gwClientsDict = countClients(onlineNodes, ["statistics", "gateway"], function (d) { |         var gwClientsDict = countClients(onlineNodes, ["statistics", "gateway"], function (d) { | ||||||
|         if (d === null) |           if (d === null) { | ||||||
|           return null |             return null; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         if (d.node) |           if (d.node) { | ||||||
|           return d.node.nodeinfo.hostname |             return d.node.nodeinfo.hostname; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         if (d.id) |           if (d.id) { | ||||||
|           return d.id |             return d.id; | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
|         return d |           return d; | ||||||
|       }) |         }); | ||||||
| 
 | 
 | ||||||
|         var siteDict = count(nodes, ["nodeinfo", "system", "site_code"], function (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) { | ||||||
|             if(d === t.site) |               if (d === t.site) { | ||||||
|               rt = t.name |                 rt = t.name; | ||||||
|           }) |  | ||||||
|         return rt |  | ||||||
|       }) |  | ||||||
| 
 |  | ||||||
|       fillTable("Status", statusTable, statusDict.sort(function (a, b) { return b[1] - a[1] })) |  | ||||||
|       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("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("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] })) |  | ||||||
|               } |               } | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |           return rt; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         fillTable("Status", statusTable, statusDict.sort(function (a, b) { | ||||||
|  |           return b[1] - a[1]; | ||||||
|  |         })); | ||||||
|  |         fillTable("Firmware", fwTable, fwDict.sort(function (a, b) { | ||||||
|  |           if (b[0] < a[0]) { | ||||||
|  |             return -1; | ||||||
|  |           } | ||||||
|  |           if (b[0] > a[0]) { | ||||||
|  |             return 1; | ||||||
|  |           } | ||||||
|  |           return 0; | ||||||
|  |         })); | ||||||
|  |         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("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("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]; | ||||||
|  |         })); | ||||||
|  |       }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|       self.render = function (el) { |       self.render = function (el) { | ||||||
|       var h2 |         var h2; | ||||||
|       self.renderSingle(el, "Status", statusTable) |         self.renderSingle(el, "Status", statusTable); | ||||||
|       self.renderSingle(el, "Nodes an Gateway", gwNodesTable) |         self.renderSingle(el, "Nodes an Gateway", gwNodesTable); | ||||||
|       self.renderSingle(el, "Clients an Gateway", gwClientsTable) |         self.renderSingle(el, "Clients an Gateway", gwClientsTable); | ||||||
|       self.renderSingle(el, "Firmwareversionen", fwTable) |         self.renderSingle(el, "Firmwareversionen", fwTable); | ||||||
|       self.renderSingle(el, "Uplink", uplinkTable) |         self.renderSingle(el, "Uplink", uplinkTable); | ||||||
|       self.renderSingle(el, "Hardwaremodelle", hwTable) |         self.renderSingle(el, "Hardwaremodelle", hwTable); | ||||||
|       self.renderSingle(el, "Auf der Karte sichtbar", geoTable) |         self.renderSingle(el, "Auf der Karte sichtbar", geoTable); | ||||||
|       self.renderSingle(el, "Autoupdater", autoTable) |         self.renderSingle(el, "Autoupdater", autoTable); | ||||||
|       self.renderSingle(el, "Site", siteTable) |         self.renderSingle(el, "Site", 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) { |       self.renderSingle = function (el, heading, table) { | ||||||
|        var h2 |         var h2; | ||||||
|        h2 = document.createElement("h2") |         h2 = document.createElement("h2"); | ||||||
|        h2.textContent = heading |         h2.textContent = heading; | ||||||
|         h2.onclick = function () { |         h2.onclick = function () { | ||||||
|          table.classList.toggle("hidden") |           table.classList.toggle("hidden"); | ||||||
|        } |         }; | ||||||
|        el.appendChild(h2) |         el.appendChild(h2); | ||||||
|        el.appendChild(table) |         el.appendChild(table); | ||||||
|      } |       }; | ||||||
|      return self |       return self; | ||||||
|   } |     }; | ||||||
| }) |   }); | ||||||
|  |  | ||||||
							
								
								
									
										250
									
								
								lib/router.js
									
										
									
									
									
								
							
							
						
						
									
										250
									
								
								lib/router.js
									
										
									
									
									
								
							|  | @ -1,214 +1,232 @@ | ||||||
| define(function () { | define(["helper"], function (helper) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function () { |   return function () { | ||||||
|     var self = this |     var self = this; | ||||||
|     var objects = { nodes: {}, links: {} } |     var objects = {nodes: {}, links: {}}; | ||||||
|     var targets = [] |     var targets = []; | ||||||
|     var views = {} |     var views = {}; | ||||||
|     var currentView |     var currentView; | ||||||
|     var currentObject |     var currentObject; | ||||||
|     var running = false |     var running = false; | ||||||
| 
 | 
 | ||||||
|     function saveState() { |     function saveState() { | ||||||
|       var e = [] |       var e = []; | ||||||
| 
 | 
 | ||||||
|       if (currentView) |       if (currentView) { | ||||||
|         e.push("v:" + currentView) |         e.push("v:" + currentView); | ||||||
| 
 |  | ||||||
|       if (currentObject) { |  | ||||||
|         if ("node" in currentObject) |  | ||||||
|           e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id)) |  | ||||||
| 
 |  | ||||||
|         if ("link" in currentObject) |  | ||||||
|           e.push("l:" + encodeURIComponent(currentObject.link.id)) |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var s = "#!" + e.join(";") |       if (currentObject) { | ||||||
|  |         if ("node" in currentObject) { | ||||||
|  |           e.push("n:" + encodeURIComponent(currentObject.node.nodeinfo.node_id)); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|       window.history.pushState(s, undefined, s) |         if ("link" in currentObject) { | ||||||
|  |           e.push("l:" + encodeURIComponent(currentObject.link.id)); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       var s = "#!" + e.join(";"); | ||||||
|  | 
 | ||||||
|  |       window.history.pushState(s, undefined, s); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function resetView(push) { |     function resetView(push) { | ||||||
|       push = trueDefault(push) |       push = helper.trueDefault(push); | ||||||
| 
 | 
 | ||||||
|       targets.forEach(function (t) { |       targets.forEach(function (t) { | ||||||
|         t.resetView() |         t.resetView(); | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       if (push) { |       if (push) { | ||||||
|         currentObject = undefined |         currentObject = undefined; | ||||||
|         saveState() |         saveState(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function gotoNode(d) { |     function gotoNode(d) { | ||||||
|       if (!d) |       if (!d) { | ||||||
|         return false |         return false; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       targets.forEach(function (t) { |       targets.forEach(function (t) { | ||||||
|         t.gotoNode(d) |         t.gotoNode(d); | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       return true |       return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function gotoLink(d) { |     function gotoLink(d) { | ||||||
|       if (!d) |       if (!d) { | ||||||
|         return false |         return false; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       targets.forEach(function (t) { |       targets.forEach(function (t) { | ||||||
|         t.gotoLink(d) |         t.gotoLink(d); | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       return true |       return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function gotoLocation(d) { |     function gotoLocation(d) { | ||||||
|       if (!d) |       if (!d) { | ||||||
|         return false |         return false; | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       targets.forEach(function (t) { |       targets.forEach(function (t) { | ||||||
|         if(!t.gotoLocation)console.warn("has no gotoLocation", t) |         if (!t.gotoLocation) { | ||||||
|         t.gotoLocation(d) |           console.warn("has no gotoLocation", t); | ||||||
|       }) |         } | ||||||
|  |         t.gotoLocation(d); | ||||||
|  |       }); | ||||||
| 
 | 
 | ||||||
|       return true |       return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function loadState(s) { |     function loadState(s) { | ||||||
|       if (!s) |       if (!s) { | ||||||
|         return false |         return false; | ||||||
| 
 |  | ||||||
|       s = decodeURIComponent(s) |  | ||||||
| 
 |  | ||||||
|       if (!s.startsWith("#!")) |  | ||||||
|         return false |  | ||||||
| 
 |  | ||||||
|       var targetSet = false |  | ||||||
| 
 |  | ||||||
|       s.slice(2).split(";").forEach(function (d) { |  | ||||||
|         var args = d.split(":") |  | ||||||
| 
 |  | ||||||
|         if (args[0] === "v" && args[1] in views) { |  | ||||||
|           currentView = args[1] |  | ||||||
|           views[args[1]]() |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|         var id |       s = decodeURIComponent(s); | ||||||
|  | 
 | ||||||
|  |       if (!s.startsWith("#!")) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       var targetSet = false; | ||||||
|  | 
 | ||||||
|  |       s.slice(2).split(";").forEach(function (d) { | ||||||
|  |         var args = d.split(":"); | ||||||
|  | 
 | ||||||
|  |         if (args[0] === "v" && args[1] in views) { | ||||||
|  |           currentView = args[1]; | ||||||
|  |           views[args[1]](); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var id; | ||||||
| 
 | 
 | ||||||
|         if (args[0] === "n") { |         if (args[0] === "n") { | ||||||
|           id = 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]); | ||||||
|             targetSet = true |             targetSet = true; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (args[0] === "l") { |         if (args[0] === "l") { | ||||||
|           id = 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]); | ||||||
|             targetSet = true |             targetSet = true; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       return targetSet |       return targetSet; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     self.start = function () { |     self.start = function () { | ||||||
|       running = true |       running = true; | ||||||
| 
 | 
 | ||||||
|       if (!loadState(window.location.hash)) |       if (!loadState(window.location.hash)) { | ||||||
|         resetView(false) |         resetView(false); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       window.onpopstate = function (d) { |       window.onpopstate = function (d) { | ||||||
|         if (!loadState(d.state)) |         if (!loadState(d.state)) { | ||||||
|           resetView(false) |           resetView(false); | ||||||
|       } |  | ||||||
|         } |         } | ||||||
|  |       }; | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     self.view = function (d) { |     self.view = function (d) { | ||||||
|       if (d in views) { |       if (d in views) { | ||||||
|         views[d]() |         views[d](); | ||||||
| 
 | 
 | ||||||
|         if (!currentView || running) |         if (!currentView || running) { | ||||||
|           currentView = d |           currentView = d; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (!running) |         if (!running) { | ||||||
|           return |           return; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         saveState() |         saveState(); | ||||||
| 
 | 
 | ||||||
|         if (!currentObject) { |         if (!currentObject) { | ||||||
|           resetView(false) |           resetView(false); | ||||||
|           return |           return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if ("node" in currentObject) |         if ("node" in currentObject) { | ||||||
|           gotoNode(currentObject.node) |           gotoNode(currentObject.node); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if ("link" in currentObject) |         if ("link" in currentObject) { | ||||||
|           gotoLink(currentObject.link) |           gotoLink(currentObject.link); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     self.node = function (d) { |     self.node = function (d) { | ||||||
|       return function () { |       return function () { | ||||||
|         if (gotoNode(d)) { |         if (gotoNode(d)) { | ||||||
|           currentObject = { node: d } |           currentObject = {node: d}; | ||||||
|           saveState() |           saveState(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return false |         return false; | ||||||
|       } |       }; | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.link = function (d) { |     self.link = function (d) { | ||||||
|       return function () { |       return function () { | ||||||
|         if (gotoLink(d)) { |         if (gotoLink(d)) { | ||||||
|           currentObject = { link: d } |           currentObject = {link: d}; | ||||||
|           saveState() |           saveState(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return false |         return false; | ||||||
|       } |       }; | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.gotoLocation = gotoLocation |     self.gotoLocation = gotoLocation; | ||||||
| 
 | 
 | ||||||
|     self.reset = function () { |     self.reset = function () { | ||||||
|       resetView() |       resetView(); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.addTarget = function (d) { |     self.addTarget = function (d) { | ||||||
|       targets.push(d) |       targets.push(d); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.removeTarget = function (d) { |     self.removeTarget = function (d) { | ||||||
|       targets = targets.filter(function (e) { |       targets = targets.filter(function (e) { | ||||||
|         return d !== e |         return d !== e; | ||||||
|       }) |       }); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.addView = function (k, d) { |     self.addView = function (k, d) { | ||||||
|       views[k] = d |       views[k] = d; | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.setData = function (data) { |     self.setData = function (data) { | ||||||
|       objects.nodes = {} |       objects.nodes = {}; | ||||||
|       objects.links = {} |       objects.links = {}; | ||||||
| 
 | 
 | ||||||
|       data.nodes.all.forEach(function (d) { |       data.nodes.all.forEach(function (d) { | ||||||
|         objects.nodes[d.nodeinfo.node_id] = d |         objects.nodes[d.nodeinfo.node_id] = d; | ||||||
|       }) |       }); | ||||||
| 
 | 
 | ||||||
|       data.graph.links.forEach(function (d) { |       data.graph.links.forEach(function (d) { | ||||||
|         objects.links[d.id] = d |         objects.links[d.id] = d; | ||||||
|       }) |       }); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     return self |     return self; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,49 +1,52 @@ | ||||||
| define([], function () { | define(function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (el) { |   return function (el) { | ||||||
|     var self = this |     var self = this; | ||||||
| 
 | 
 | ||||||
|     var sidebar = document.createElement("div") |     var sidebar = document.createElement("div"); | ||||||
|     sidebar.classList.add("sidebar") |     sidebar.classList.add("sidebar"); | ||||||
|     el.appendChild(sidebar) |     el.appendChild(sidebar); | ||||||
| 
 | 
 | ||||||
|     var button = document.createElement("button") |     var button = document.createElement("button"); | ||||||
|     sidebar.appendChild(button) |     sidebar.appendChild(button); | ||||||
| 
 | 
 | ||||||
|     button.classList.add("sidebarhandle") |     button.classList.add("sidebarhandle"); | ||||||
|     button.onclick = function () { |     button.onclick = function () { | ||||||
|       sidebar.classList.toggle("hidden") |       sidebar.classList.toggle("hidden"); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     var container = document.createElement("div") |     var container = document.createElement("div"); | ||||||
|     container.classList.add("container") |     container.classList.add("container"); | ||||||
|     sidebar.appendChild(container) |     sidebar.appendChild(container); | ||||||
| 
 | 
 | ||||||
|     self.getWidth = function () { |     self.getWidth = function () { | ||||||
|       if (sidebar.classList.contains("hidden")) |       if (sidebar.classList.contains("hidden")) { | ||||||
|           return 0 |         return 0; | ||||||
| 
 |  | ||||||
|       var small = window.matchMedia("(max-width: 630pt)") |  | ||||||
|       return small.matches ? 0 : sidebar.offsetWidth |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       var small = window.matchMedia("(max-width: 630pt)"); | ||||||
|  |       return small.matches ? 0 : sidebar.offsetWidth; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     self.add = function (d) { |     self.add = function (d) { | ||||||
|       d.render(container) |       d.render(container); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.ensureVisible = function () { |     self.ensureVisible = function () { | ||||||
|       sidebar.classList.remove("hidden") |       sidebar.classList.remove("hidden"); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.hide = function () { |     self.hide = function () { | ||||||
|       container.classList.add("hidden") |       container.classList.add("hidden"); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.reveal = function () { |     self.reveal = function () { | ||||||
|       container.classList.remove("hidden") |       container.classList.remove("hidden"); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.container = sidebar |     self.container = sidebar; | ||||||
| 
 | 
 | ||||||
|     return self |     return self; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
|  | @ -1,63 +1,68 @@ | ||||||
| define(["moment", "virtual-dom"], function (moment, V) { | define(["moment", "virtual-dom", "helper", "moment.de"], function (moment, V, helper) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (nodes, field, router, title) { |   return function (nodes, field, router, title) { | ||||||
|     var self = this |     var self = this; | ||||||
|     var el, tbody |     var el, tbody; | ||||||
| 
 | 
 | ||||||
|     self.render = function (d) { |     self.render = function (d) { | ||||||
|       el = document.createElement("div") |       el = document.createElement("div"); | ||||||
|       d.appendChild(el) |       d.appendChild(el); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     self.setData = function (data) { |     self.setData = function (data) { | ||||||
|       var list = data.nodes[nodes] |       var list = data.nodes[nodes]; | ||||||
| 
 | 
 | ||||||
|       if (list.length === 0) { |       if (list.length === 0) { | ||||||
|         while (el.firstChild) |         while (el.firstChild) { | ||||||
|               el.removeChild(el.firstChild) |           el.removeChild(el.firstChild); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         tbody = null |         tbody = null; | ||||||
| 
 | 
 | ||||||
|         return |         return; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (!tbody) { |       if (!tbody) { | ||||||
|         var h2 = document.createElement("h2") |         var h2 = document.createElement("h2"); | ||||||
|         h2.textContent = title |         h2.textContent = title; | ||||||
|         el.appendChild(h2) |         el.appendChild(h2); | ||||||
| 
 | 
 | ||||||
|         var table = document.createElement("table") |         var table = document.createElement("table"); | ||||||
|         el.appendChild(table) |         el.appendChild(table); | ||||||
| 
 | 
 | ||||||
|         tbody = document.createElement("tbody") |         tbody = document.createElement("tbody"); | ||||||
|         tbody.last = V.h("tbody") |         tbody.last = V.h("tbody"); | ||||||
|         table.appendChild(tbody) |         table.appendChild(tbody); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       var items = list.map(function (d) { |       var items = list.map(function (d) { | ||||||
|         var time = moment(d[field]).from(data.now) |         var time = moment(d[field]).from(data.now); | ||||||
|         var td1Content = [] |         var td1Content = []; | ||||||
| 
 | 
 | ||||||
|         var aClass = ["hostname", d.flags.online ? "online" : "offline"] |         var aClass = ["hostname", d.flags.online ? "online" : "offline"]; | ||||||
| 
 | 
 | ||||||
|         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: "#!n:" + d.nodeinfo.node_id |           href: "#!n:" + d.nodeinfo.node_id | ||||||
|                                  }, d.nodeinfo.hostname)) |         }, d.nodeinfo.hostname)); | ||||||
| 
 | 
 | ||||||
|         if (has_location(d)) |         if (helper.hasLocation(d)) { | ||||||
|           td1Content.push(V.h("span", {className: "icon ion-location"})) |           td1Content.push(V.h("span", {className: "icon ion-location"})); | ||||||
| 
 |  | ||||||
|         var td1 = V.h("td", td1Content) |  | ||||||
|         var td2 = V.h("td", time) |  | ||||||
| 
 |  | ||||||
|         return V.h("tr", [td1, td2]) |  | ||||||
|       }) |  | ||||||
| 
 |  | ||||||
|       var tbodyNew = V.h("tbody", items) |  | ||||||
|       tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew)) |  | ||||||
|       tbody.last = tbodyNew |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     return self |         var td1 = V.h("td", td1Content); | ||||||
|   } |         var td2 = V.h("td", time); | ||||||
| }) | 
 | ||||||
|  |         return V.h("tr", [td1, td2]); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |       var tbodyNew = V.h("tbody", items); | ||||||
|  |       tbody = V.patch(tbody, V.diff(tbody.last, tbodyNew)); | ||||||
|  |       tbody.last = tbodyNew; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return self; | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | @ -1,57 +1,64 @@ | ||||||
| define(["virtual-dom"], function (V) { | define(["virtual-dom"], function (V) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (headings, sortIndex, renderRow) { |   return function (headings, sortIndex, renderRow) { | ||||||
|     var data |     var data; | ||||||
|     var sortReverse = false |     var sortReverse = false; | ||||||
|     var el = document.createElement("table") |     var el = document.createElement("table"); | ||||||
|     var elLast = V.h("table") |     var elLast = V.h("table"); | ||||||
| 
 | 
 | ||||||
|     function sortTable(i) { |     function sortTable(i) { | ||||||
|       sortReverse = i === sortIndex ? !sortReverse : false |       sortReverse = i === sortIndex ? !sortReverse : false; | ||||||
|       sortIndex = i |       sortIndex = i; | ||||||
| 
 | 
 | ||||||
|       updateView() |       updateView(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function sortTableHandler(i) { |     function sortTableHandler(i) { | ||||||
|       return function () { sortTable(i) } |       return function () { | ||||||
|  |         sortTable(i); | ||||||
|  |       }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function updateView() { |     function updateView() { | ||||||
|       var children = [] |       var children = []; | ||||||
| 
 | 
 | ||||||
|       if (data.length !== 0) { |       if (data.length !== 0) { | ||||||
|         var th = headings.map(function (d, i) { |         var th = headings.map(function (d, i) { | ||||||
|           var properties = { onclick: sortTableHandler(i), |           var properties = { | ||||||
|  |             onclick: sortTableHandler(i), | ||||||
|             className: "sort-header" |             className: "sort-header" | ||||||
|  |           }; | ||||||
|  | 
 | ||||||
|  |           if (sortIndex === i) { | ||||||
|  |             properties.className += sortReverse ? " sort-up" : " sort-down"; | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           if (sortIndex === i) |           return V.h("th", properties, d.name); | ||||||
|             properties.className += sortReverse ? " sort-up" : " sort-down" |         }); | ||||||
| 
 | 
 | ||||||
|           return V.h("th", properties, d.name) |         var links = data.slice(0).sort(headings[sortIndex].sort); | ||||||
|         }) |  | ||||||
| 
 | 
 | ||||||
|         var links = data.slice(0).sort(headings[sortIndex].sort) |         if (headings[sortIndex].reverse ? !sortReverse : sortReverse) { | ||||||
| 
 |           links = links.reverse(); | ||||||
|         if (headings[sortIndex].reverse ? !sortReverse : sortReverse) |  | ||||||
|           links = links.reverse() |  | ||||||
| 
 |  | ||||||
|         children.push(V.h("thead", V.h("tr", th))) |  | ||||||
|         children.push(V.h("tbody", links.map(renderRow))) |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|       var elNew = V.h("table", children) |         children.push(V.h("thead", V.h("tr", th))); | ||||||
|       el = V.patch(el, V.diff(elLast, elNew)) |         children.push(V.h("tbody", links.map(renderRow))); | ||||||
|       elLast = elNew |       } | ||||||
|  | 
 | ||||||
|  |       var elNew = V.h("table", children); | ||||||
|  |       el = V.patch(el, V.diff(elLast, elNew)); | ||||||
|  |       elLast = elNew; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.setData = function (d) { |     this.setData = function (d) { | ||||||
|       data = d |       data = d; | ||||||
|       updateView() |       updateView(); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     this.el = el |     this.el = el; | ||||||
| 
 | 
 | ||||||
|     return this |     return this; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
							
								
								
									
										72
									
								
								lib/tabs.js
									
										
									
									
									
								
							
							
						
						
									
										72
									
								
								lib/tabs.js
									
										
									
									
									
								
							|  | @ -1,57 +1,63 @@ | ||||||
| define([], function () { | define(function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function () { |   return function () { | ||||||
|     var self = this |     var self = this; | ||||||
| 
 | 
 | ||||||
|     var tabs = document.createElement("ul") |     var tabs = document.createElement("ul"); | ||||||
|     tabs.classList.add("tabs") |     tabs.classList.add("tabs"); | ||||||
| 
 | 
 | ||||||
|     var container = document.createElement("div") |     var container = document.createElement("div"); | ||||||
| 
 | 
 | ||||||
|     function gotoTab(li) { |     function gotoTab(li) { | ||||||
|       for (var i = 0; i < tabs.children.length; i++) |       for (var i = 0; i < tabs.children.length; i++) { | ||||||
|         tabs.children[i].classList.remove("visible") |         tabs.children[i].classList.remove("visible"); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       while (container.firstChild) |       while (container.firstChild) { | ||||||
|         container.removeChild(container.firstChild) |         container.removeChild(container.firstChild); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       li.classList.add("visible") |       li.classList.add("visible"); | ||||||
| 
 | 
 | ||||||
|       var tab = document.createElement("div") |       var tab = document.createElement("div"); | ||||||
|       tab.classList.add("tab") |       tab.classList.add("tab"); | ||||||
|       container.appendChild(tab) |       container.appendChild(tab); | ||||||
|       li.child.render(tab) |       li.child.render(tab); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function switchTab() { |     function switchTab() { | ||||||
|       gotoTab(this) |       gotoTab(this); | ||||||
| 
 | 
 | ||||||
|       return false |       return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     self.add = function (title, d) { |     self.add = function (title, d) { | ||||||
|       var li = document.createElement("li") |       var li = document.createElement("li"); | ||||||
|       li.textContent = title |       li.textContent = title; | ||||||
|       li.onclick = switchTab |       li.onclick = switchTab; | ||||||
|       li.child = d |       li.child = d; | ||||||
|       tabs.appendChild(li) |       tabs.appendChild(li); | ||||||
| 
 | 
 | ||||||
|       var anyVisible = false |       var anyVisible = false; | ||||||
| 
 | 
 | ||||||
|       for (var i = 0; i < tabs.children.length; i++) |       for (var i = 0; i < tabs.children.length; i++) { | ||||||
|         if (tabs.children[i].classList.contains("visible")) { |         if (tabs.children[i].classList.contains("visible")) { | ||||||
|           anyVisible = true |           anyVisible = true; | ||||||
|           break |           break; | ||||||
|  |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (!anyVisible) |       if (!anyVisible) { | ||||||
|         gotoTab(li) |         gotoTab(li); | ||||||
|       } |       } | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     self.render = function (el) { |     self.render = function (el) { | ||||||
|       el.appendChild(tabs) |       el.appendChild(tabs); | ||||||
|       el.appendChild(container) |       el.appendChild(container); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     return self |     return self; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								lib/title.js
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								lib/title.js
									
										
									
									
									
								
							|  | @ -1,35 +1,40 @@ | ||||||
| define(function () { | define(function () { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   return function (config) { |   return function (config) { | ||||||
|     function setTitle(d) { |     function setTitle(d) { | ||||||
|       var title = [config.siteName] |       var title = [config.siteName]; | ||||||
| 
 | 
 | ||||||
|       if (d !== undefined) |       if (d !== undefined) { | ||||||
|         title.push(d) |         title.push(d); | ||||||
|  |       } | ||||||
| 
 | 
 | ||||||
|       document.title = title.join(": ") |       document.title = title.join(": "); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.resetView = function () { |     this.resetView = function () { | ||||||
|       setTitle() |       setTitle(); | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     this.gotoNode = function (d) { |     this.gotoNode = function (d) { | ||||||
|       if (d) |       if (d) { | ||||||
|         setTitle(d.nodeinfo.hostname) |         setTitle(d.nodeinfo.hostname); | ||||||
|       } |       } | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     this.gotoLink = function (d) { |     this.gotoLink = function (d) { | ||||||
|       if (d) |       if (d) { | ||||||
|         setTitle((d.source.node ? d.source.node.nodeinfo.hostname : d.source.id) + " – " + 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 () { | ||||||
|       //ignore
 |       //ignore
 | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     this.destroy = function () { |     this.destroy = function () { | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
|     return this |     return this; | ||||||
|   } |   }; | ||||||
| }) | }); | ||||||
|  |  | ||||||
							
								
								
									
										243
									
								
								lib/utils/helper.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								lib/utils/helper.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,243 @@ | ||||||
|  | "use strict"; | ||||||
|  | 
 | ||||||
|  | define({ | ||||||
|  |   get: function (url) { | ||||||
|  |     return new Promise(function (resolve, reject) { | ||||||
|  |       var req = new XMLHttpRequest(); | ||||||
|  |       req.open("GET", url); | ||||||
|  | 
 | ||||||
|  |       req.onload = function () { | ||||||
|  |         if (req.status == 200) { | ||||||
|  |           resolve(req.response); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |           reject(Error(req.statusText)); | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       req.onerror = function () { | ||||||
|  |         reject(Error("Network Error")); | ||||||
|  |       }; | ||||||
|  | 
 | ||||||
|  |       req.send(); | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   getJSON: function (url) { | ||||||
|  |     return require("helper").get(url).then(JSON.parse); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   sortByKey: function (key, d) { | ||||||
|  |     return d.slice().sort(function (a, b) { | ||||||
|  |       return a[key] - b[key]; | ||||||
|  |     }).reverse(); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   limit: function (key, m, d) { | ||||||
|  |     return d.filter(function (d) { | ||||||
|  |       return d[key].isAfter(m); | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   sum: function (a) { | ||||||
|  |     return a.reduce(function (a, b) { | ||||||
|  |       return a + b; | ||||||
|  |     }, 0); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   one: function () { | ||||||
|  |     return 1; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   trueDefault: function (d) { | ||||||
|  |     return d === undefined ? true : d; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   dictGet: function (dict, key) { | ||||||
|  |     var k = key.shift(); | ||||||
|  | 
 | ||||||
|  |     if (!(k in dict)) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (key.length == 0) { | ||||||
|  |       return dict[k]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return this.dictGet(dict[k], key); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   localStorageTest: function () { | ||||||
|  |     var test = "test"; | ||||||
|  |     try { | ||||||
|  |       localStorage.setItem(test, test); | ||||||
|  |       localStorage.removeItem(test); | ||||||
|  |       return true; | ||||||
|  |     } catch (e) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   listReplace: function (s, subst) { | ||||||
|  |     for (var key in subst) { | ||||||
|  |       var re = new RegExp(key, "g"); | ||||||
|  |       s = s.replace(re, subst[key]); | ||||||
|  |     } | ||||||
|  |     return s; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   /* Helpers working with nodes */ | ||||||
|  | 
 | ||||||
|  |   offline: function (d) { | ||||||
|  |     return !d.flags.online; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   online: function (d) { | ||||||
|  |     return d.flags.online; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   hasLocation: function (d) { | ||||||
|  |     return "location" in d.nodeinfo && | ||||||
|  |       Math.abs(d.nodeinfo.location.latitude) < 90 && | ||||||
|  |       Math.abs(d.nodeinfo.location.longitude) < 180; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   subtract: function (a, b) { | ||||||
|  |     var ids = {}; | ||||||
|  | 
 | ||||||
|  |     b.forEach(function (d) { | ||||||
|  |       ids[d.nodeinfo.node_id] = true; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return a.filter(function (d) { | ||||||
|  |       return !(d.nodeinfo.node_id in ids); | ||||||
|  |     }); | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   /* Helpers working with links */ | ||||||
|  | 
 | ||||||
|  |   showDistance: function (d) { | ||||||
|  |     if (isNaN(d.distance)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return d.distance.toFixed(0) + " m"; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   showTq: function (d) { | ||||||
|  |     return (1 / d.tq * 100).toFixed(0) + "%"; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   attributeEntry: function (el, label, value) { | ||||||
|  |     if (value === null || value == undefined) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var tr = document.createElement("tr"); | ||||||
|  |     var th = document.createElement("th"); | ||||||
|  | 
 | ||||||
|  |     if (typeof label === "string") { | ||||||
|  |       th.textContent = label; | ||||||
|  |     } else { | ||||||
|  |       th.appendChild(label); | ||||||
|  |       tr.classList.add("routerpic"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     tr.appendChild(th); | ||||||
|  | 
 | ||||||
|  |     var td = document.createElement("td"); | ||||||
|  | 
 | ||||||
|  |     if (typeof value == "function") { | ||||||
|  |       value(td); | ||||||
|  |     } else { | ||||||
|  |       td.appendChild(document.createTextNode(value)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     tr.appendChild(td); | ||||||
|  | 
 | ||||||
|  |     el.appendChild(tr); | ||||||
|  | 
 | ||||||
|  |     return td; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   createIframe: function (opt, width, height) { | ||||||
|  |     var el = document.createElement("iframe"); | ||||||
|  |     width = typeof width !== "undefined" ? width : "525px"; | ||||||
|  |     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; | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   showStat: function (o, subst) { | ||||||
|  |     var content, caption; | ||||||
|  |     subst = typeof subst !== "undefined" ? subst : {}; | ||||||
|  | 
 | ||||||
|  |     if (o.thumbnail) { | ||||||
|  |       content = document.createElement("img"); | ||||||
|  |       content.src = require("helper").listReplace(o.thumbnail, subst); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (o.caption) { | ||||||
|  |       caption = require("helper").listReplace(o.caption, subst); | ||||||
|  | 
 | ||||||
|  |       if (!content) { | ||||||
|  |         content = document.createTextNode(caption); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (o.iframe) { | ||||||
|  |       content = require("helper").createIframe(o.iframe, o.width, o.height); | ||||||
|  |       if (o.iframe.src) { | ||||||
|  |         content.src = require("helper").listReplace(o.iframe.src, subst); | ||||||
|  |       } else { | ||||||
|  |         content.src = require("helper").listReplace(o.iframe, subst); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var p = document.createElement("p"); | ||||||
|  | 
 | ||||||
|  |     if (o.href) { | ||||||
|  |       var link = document.createElement("a"); | ||||||
|  |       link.target = "_blank"; | ||||||
|  |       link.href = require("helper").listReplace(o.href, subst); | ||||||
|  |       link.appendChild(content); | ||||||
|  | 
 | ||||||
|  |       if (caption && o.thumbnail) { | ||||||
|  |         link.title = caption; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       p.appendChild(link); | ||||||
|  |     } else { | ||||||
|  |       p.appendChild(content); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return p; | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | @ -1,60 +0,0 @@ | ||||||
| define([], function () { |  | ||||||
|   function order(c) { |  | ||||||
|     if (/^\d$/.test(c)) |  | ||||||
|       return 0 |  | ||||||
|     else if (/^[a-z]$/i.test(c)) |  | ||||||
|       return c.charCodeAt(0) |  | ||||||
|     else if (c === "~") |  | ||||||
|       return -1 |  | ||||||
|     else if (c) |  | ||||||
|       return c.charCodeAt(0) + 256 |  | ||||||
|     else |  | ||||||
|       return 0 |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // Based on dpkg code
 |  | ||||||
|   function vercomp(a, b) { |  | ||||||
|     var apos = 0, bpos = 0 |  | ||||||
|     while (apos < a.length || bpos < b.length) { |  | ||||||
|       var firstDiff = 0 |  | ||||||
| 
 |  | ||||||
|       while ((apos < a.length && !/^\d$/.test(a[apos])) || (bpos < b.length && !/^\d$/.test(b[bpos]))) { |  | ||||||
|         var ac = order(a[apos]) |  | ||||||
|         var bc = order(b[bpos]) |  | ||||||
| 
 |  | ||||||
|         if (ac !== bc) |  | ||||||
|           return ac - bc |  | ||||||
| 
 |  | ||||||
|         apos++ |  | ||||||
|         bpos++ |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       while (a[apos] === "0") |  | ||||||
|         apos++ |  | ||||||
| 
 |  | ||||||
|       while (b[bpos] === "0") |  | ||||||
|         bpos++ |  | ||||||
| 
 |  | ||||||
|       while (/^\d$/.test(a[apos]) && /^\d$/.test(b[bpos])) { |  | ||||||
|         if (firstDiff === 0) |  | ||||||
|           firstDiff = a.charCodeAt(apos) - b.charCodeAt(bpos) |  | ||||||
| 
 |  | ||||||
|         apos++ |  | ||||||
|         bpos++ |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (/^\d$/.test(a[apos])) |  | ||||||
|         return 1 |  | ||||||
| 
 |  | ||||||
|       if (/^\d$/.test(b[bpos])) |  | ||||||
|         return -1 |  | ||||||
| 
 |  | ||||||
|       if (firstDiff !== 0) |  | ||||||
|         return firstDiff |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return 0 |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return vercomp |  | ||||||
| }) |  | ||||||
							
								
								
									
										75
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										75
									
								
								package.json
									
										
									
									
									
								
							|  | @ -1,41 +1,34 @@ | ||||||
| { | { | ||||||
|   "name": "hopglass", |   "name": "hopglass", | ||||||
|   "version": "1.0.0", |   "license": "AGPL-3.0", | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "https://github.com/hopglass/hopglass.git" | ||||||
|  |   }, | ||||||
|  |   "bugs": { | ||||||
|  |     "url": "https://github.com/hopglass/hopglass/issues" | ||||||
|  |   }, | ||||||
|   "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.6", | ||||||
|     "dart-sass": "^1.16.1", |     "eslint": "^2.11.0", | ||||||
|     "grunt": "^1.0.3", |     "eslint-config-defaults": "^9.0.0", | ||||||
|     "grunt-check-dependencies": "^0.6.0", |     "grunt": "^1.0.1", | ||||||
|     "grunt-contrib-clean": "^0.6.0", |     "grunt-bower-install-simple": "^1.2.3", | ||||||
|     "grunt-contrib-connect": "^0.8.0", |     "grunt-check-dependencies": "^0.12.0", | ||||||
|     "grunt-contrib-copy": "^0.5.0", |     "grunt-contrib-clean": "^1.0.0", | ||||||
|     "grunt-contrib-cssmin": "^0.12.2", |     "grunt-contrib-connect": "^1.0.2", | ||||||
|     "grunt-contrib-requirejs": "^0.4.4", |     "grunt-contrib-copy": "^1.0.0", | ||||||
|     "grunt-contrib-uglify": "^0.5.1", |     "grunt-contrib-cssmin": "^1.0.1", | ||||||
|     "grunt-contrib-watch": "^0.6.1", |     "grunt-contrib-requirejs": "^1.0.0", | ||||||
|     "grunt-eslint": "^10.0.0", |     "grunt-contrib-uglify": "^1.0.1", | ||||||
|     "grunt-git-describe": "^2.3.2", |     "grunt-contrib-watch": "^1.0.0", | ||||||
|     "grunt-postcss": "^0.7.2", |     "grunt-eslint": "^18.1.0", | ||||||
|     "grunt-sass": "^3.0.2" |     "grunt-inline": "^0.3.6", | ||||||
|   }, |     "grunt-postcss": "^0.8.0", | ||||||
|   "dependencies": { |     "grunt-sass": "^1.2.0" | ||||||
|     "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": { | ||||||
|  | @ -43,24 +36,6 @@ | ||||||
|       "amd": true, |       "amd": true, | ||||||
|       "es6": true, |       "es6": true, | ||||||
|       "node": true |       "node": true | ||||||
|     }, |  | ||||||
|     "globals": { |  | ||||||
|       "showStat": false, |  | ||||||
|       "attributeEntry": false, |  | ||||||
|       "dictGet": false, |  | ||||||
|       "getJSON": false, |  | ||||||
|       "has_location": false, |  | ||||||
|       "limit": false, |  | ||||||
|       "localStorageTest": false, |  | ||||||
|       "offline": false, |  | ||||||
|       "one": false, |  | ||||||
|       "online": false, |  | ||||||
|       "showDistance": false, |  | ||||||
|       "showTq": false, |  | ||||||
|       "sortByKey": false, |  | ||||||
|       "subtract": false, |  | ||||||
|       "sum": false, |  | ||||||
|       "trueDefault": false |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								scss/_leaflet-layer.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								scss/_leaflet-layer.scss
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | .leaflet-control-layers { | ||||||
|  |   box-shadow: none; | ||||||
|  |   border-radius: 0; | ||||||
|  |   background: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .leaflet-control-layers-toggle { | ||||||
|  |   background: none; | ||||||
|  |   &::before { | ||||||
|  |     content: "\f229"; | ||||||
|  |     display: inline-block; | ||||||
|  |     font-family: "ionicons" !important; | ||||||
|  |     font-size: 2.3rem; | ||||||
|  |     speak: none; | ||||||
|  |     text-rendering: auto; | ||||||
|  |     line-height: 1; | ||||||
|  |     -webkit-font-smoothing: antialiased; | ||||||
|  |     -moz-osx-font-smoothing: grayscale; | ||||||
|  |     color: #e32d6d; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .leaflet-control-layers-expanded { | ||||||
|  |   padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .leaflet-control-layers-list { | ||||||
|  |   background: rgba(255, 255, 255, 0.9); | ||||||
|  |   padding: 10px; | ||||||
|  |   label { | ||||||
|  |     cursor: pointer; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -1 +1 @@ | ||||||
| ../node_modules/leaflet-label/dist/leaflet.label.css | ../bower_components/Leaflet.label/dist/leaflet.label.css | ||||||
|  | @ -1 +1 @@ | ||||||
| ../node_modules/leaflet/dist/leaflet.css | ../bower_components/leaflet/dist/leaflet.css | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| @import '_base'; | @import '_base'; | ||||||
| @import '_leaflet'; | @import '_leaflet'; | ||||||
| @import '_leaflet.label'; | @import '_leaflet.label'; | ||||||
|  | @import '_leaflet-layer'; | ||||||
| @import '_filters'; | @import '_filters'; | ||||||
| @import '_loader'; | @import '_loader'; | ||||||
| 
 | 
 | ||||||
|  | @ -70,7 +71,7 @@ $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; |   color: #333; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,13 +1,10 @@ | ||||||
| module.exports = function (grunt) { | module.exports = function (grunt) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   grunt.config.merge({ |   grunt.config.merge({ | ||||||
|     nodedir: "node_modules", |     bowerdir: "bower_components", | ||||||
|     copy: { |     copy: { | ||||||
|       html: { |       html: { | ||||||
|         options: { |  | ||||||
|           process: function (content) { |  | ||||||
|             return content.replace("#revision#", grunt.option("gitRevision")) |  | ||||||
|           } |  | ||||||
|         }, |  | ||||||
|         src: ["*.html"], |         src: ["*.html"], | ||||||
|         expand: true, |         expand: true, | ||||||
|         cwd: "html/", |         cwd: "html/", | ||||||
|  | @ -22,39 +19,44 @@ module.exports = function(grunt) { | ||||||
|         src: ["es6-shim/es6-shim.min.js", |         src: ["es6-shim/es6-shim.min.js", | ||||||
|           "es6-shim/es6-shim.map"], |           "es6-shim/es6-shim.map"], | ||||||
|         expand: true, |         expand: true, | ||||||
|         cwd: "node_modules/", |         cwd: "bower_components/", | ||||||
|         dest: "build/vendor/" |         dest: "build/vendor/" | ||||||
|       }, |       }, | ||||||
|       roboto: { |       robotoSlab: { | ||||||
|         src: [ "fonts/roboto/*", |         src: ["fonts/*", | ||||||
|                "fonts/roboto-slab/*", |           "roboto-slab-fontface.css" | ||||||
|                "css/roboto/roboto-fontface.css", |  | ||||||
|                "css/roboto-slab/roboto-slab-fontface.css" |  | ||||||
|         ], |         ], | ||||||
|         expand: true, |         expand: true, | ||||||
|         dest: "build/", |         dest: "build/", | ||||||
|         cwd: "node_modules/roboto-fontface/" |         cwd: "bower_components/roboto-slab-fontface" | ||||||
|  |       }, | ||||||
|  |       roboto: { | ||||||
|  |         src: ["fonts/*", | ||||||
|  |           "roboto-fontface.css" | ||||||
|  |         ], | ||||||
|  |         expand: true, | ||||||
|  |         dest: "build/", | ||||||
|  |         cwd: "bower_components/roboto-fontface" | ||||||
|       }, |       }, | ||||||
|       ionicons: { |       ionicons: { | ||||||
|         src: ["fonts/*", |         src: ["fonts/*", | ||||||
|                "css/ionicons.min.css" |           "hopglass-icons.css" | ||||||
|         ], |         ], | ||||||
|         expand: true, |         expand: true, | ||||||
|         dest: "build/", |         dest: "build/", | ||||||
|         cwd: "node_modules/ionicons/" |         cwd: "assets/icons/" | ||||||
|       }, |       }, | ||||||
|       leafletImages: { |       leafletImages: { | ||||||
|         src: ["images/*"], |         src: ["images/*"], | ||||||
|         expand: true, |         expand: true, | ||||||
|         dest: "build/", |         dest: "build/", | ||||||
|         cwd: "node_modules/leaflet/dist/" |         cwd: "bower_components/leaflet/dist/" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     sass: { |     sass: { | ||||||
|       options: { |       options: { | ||||||
|         sourceMap: true, |         sourceMap: true, | ||||||
|         outputStyle: "compressed", |         outputStyle: "compressed" | ||||||
|         implementation: require("dart-sass") |  | ||||||
|       }, |       }, | ||||||
|       dist: { |       dist: { | ||||||
|         files: { |         files: { | ||||||
|  | @ -78,30 +80,65 @@ module.exports = function(grunt) { | ||||||
|     cssmin: { |     cssmin: { | ||||||
|       target: { |       target: { | ||||||
|         files: { |         files: { | ||||||
|           "build/style.css": [ "node_modules/leaflet/dist/leaflet.css", |           "build/style.css": ["bower_components/leaflet/dist/leaflet.css", | ||||||
|                                "node_modules/leaflet-label/dist/leaflet.label.css", |             "bower_components/Leaflet.label/dist/leaflet.label.css", | ||||||
|             "style.css" |             "style.css" | ||||||
|           ] |           ] | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     inline: { | ||||||
|  |       dist: { | ||||||
|  |         options: { | ||||||
|  |           cssmin: true, | ||||||
|  |           uglify: true | ||||||
|  |         }, | ||||||
|  |         src: "build/index.html", | ||||||
|  |         dest: "build/index.html" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "bower-install-simple": { | ||||||
|  |       options: { | ||||||
|  |         directory: "<%=bowerdir%>", | ||||||
|  |         color: true, | ||||||
|  |         interactive: false, | ||||||
|  |         production: true | ||||||
|  |       }, | ||||||
|  |       "prod": { | ||||||
|  |         options: { | ||||||
|  |           production: true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     requirejs: { |     requirejs: { | ||||||
|       compile: { |       default: { | ||||||
|         options: { |         options: { | ||||||
|           baseUrl: "lib", |           baseUrl: "lib", | ||||||
|           name: "../node_modules/almond/almond", |           name: "../bower_components/almond/almond", | ||||||
|           mainConfigFile: "app.js", |           mainConfigFile: "app.js", | ||||||
|           include: "../app", |           include: "../app", | ||||||
|           wrap: true, |           out: "build/app.js", | ||||||
|           optimize: "uglify", |           build: true | ||||||
|           out: "build/app.js" |         } | ||||||
|  |       }, | ||||||
|  |       dev: { | ||||||
|  |         options: { | ||||||
|  |           baseUrl: "lib", | ||||||
|  |           name: "../bower_components/almond/almond", | ||||||
|  |           mainConfigFile: "app.js", | ||||||
|  |           include: "../app", | ||||||
|  |           optimize: "none", | ||||||
|  |           out: "build/app.js", | ||||||
|  |           build: false | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }) |   }); | ||||||
| 
 | 
 | ||||||
|   grunt.loadNpmTasks("grunt-contrib-copy") |   grunt.loadNpmTasks("grunt-bower-install-simple"); | ||||||
|   grunt.loadNpmTasks("grunt-contrib-requirejs") |   grunt.loadNpmTasks("grunt-contrib-copy"); | ||||||
|   grunt.loadNpmTasks("grunt-sass") |   grunt.loadNpmTasks("grunt-contrib-requirejs"); | ||||||
|   grunt.loadNpmTasks("grunt-postcss") |   grunt.loadNpmTasks("grunt-sass"); | ||||||
| } |   grunt.loadNpmTasks("grunt-postcss"); | ||||||
|  |   grunt.loadNpmTasks("grunt-inline"); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @ -1,9 +1,11 @@ | ||||||
| module.exports = function (grunt) { | module.exports = function (grunt) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   grunt.config.merge({ |   grunt.config.merge({ | ||||||
|     clean: { |     clean: { | ||||||
|       build: ["build/**/*", "node_modules/grunt-newer/.cache"] |       build: ["build/**/*", "node_modules/grunt-newer/.cache"] | ||||||
|     } |     } | ||||||
|   }) |   }); | ||||||
| 
 | 
 | ||||||
|   grunt.loadNpmTasks("grunt-contrib-clean") |   grunt.loadNpmTasks("grunt-contrib-clean"); | ||||||
| } | }; | ||||||
|  |  | ||||||
|  | @ -1,9 +1,16 @@ | ||||||
| module.exports = function (grunt) { | module.exports = function (grunt) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   grunt.config.merge({ |   grunt.config.merge({ | ||||||
|     connect: { |     connect: { | ||||||
|       server: { |       server: { | ||||||
|         options: { |         options: { | ||||||
|           base: "build/", //TODO: once grunt-contrib-connect 0.9 is released, set index file
 |           base: { | ||||||
|  |             path: "build", | ||||||
|  |             options: { | ||||||
|  |               index: "index.html" | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|           livereload: true |           livereload: true | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | @ -13,8 +20,8 @@ module.exports = function (grunt) { | ||||||
|         options: { |         options: { | ||||||
|           livereload: true |           livereload: true | ||||||
|         }, |         }, | ||||||
|         files: ["*.css", "app.js", "lib/**/*.js", "*.html"], |         files: ["*.css", "app.js", "lib/**/*.js", "*.html", "scss/**/*.scss"], | ||||||
|         tasks: ["default"] |         tasks: ["dev"] | ||||||
|       }, |       }, | ||||||
|       config: { |       config: { | ||||||
|         options: { |         options: { | ||||||
|  | @ -24,8 +31,8 @@ module.exports = function (grunt) { | ||||||
|         tasks: [] |         tasks: [] | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }) |   }); | ||||||
| 
 | 
 | ||||||
|   grunt.loadNpmTasks("grunt-contrib-connect") |   grunt.loadNpmTasks("grunt-contrib-connect"); | ||||||
|   grunt.loadNpmTasks("grunt-contrib-watch") |   grunt.loadNpmTasks("grunt-contrib-watch"); | ||||||
| } | }; | ||||||
|  |  | ||||||
|  | @ -1,24 +1,19 @@ | ||||||
| module.exports = function (grunt) { | module.exports = function (grunt) { | ||||||
|  |   "use strict"; | ||||||
|  | 
 | ||||||
|   grunt.config.merge({ |   grunt.config.merge({ | ||||||
|     checkDependencies: { |     checkDependencies: { | ||||||
|       options: { |       options: { | ||||||
|         install: true |         install: true | ||||||
|       }, |       }, | ||||||
|  |       bower: { | ||||||
|  |         options: { | ||||||
|  |           packageManager: "bower" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|       npm: {} |       npm: {} | ||||||
|     }, |     }, | ||||||
|     eslint: { |     eslint: { | ||||||
|       options: { |  | ||||||
|         rules: { |  | ||||||
|           "semi": [2, "never"], |  | ||||||
|           "curly": [2, "multi"], |  | ||||||
|           "strict": [2, "never"], |  | ||||||
|           "no-multi-spaces": 0, |  | ||||||
|           "no-new": 0, |  | ||||||
|           "no-shadow": 0, |  | ||||||
|           "no-use-before-define": [1, "nofunc"], |  | ||||||
|           "no-underscore-dangle": 0 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       sources: { |       sources: { | ||||||
|         src: ["app.js", "!Gruntfile.js", "lib/**/*.js"] |         src: ["app.js", "!Gruntfile.js", "lib/**/*.js"] | ||||||
|       }, |       }, | ||||||
|  | @ -26,8 +21,8 @@ module.exports = function (grunt) { | ||||||
|         src: ["Gruntfile.js", "tasks/*.js"] |         src: ["Gruntfile.js", "tasks/*.js"] | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   }) |   }); | ||||||
| 
 | 
 | ||||||
|   grunt.loadNpmTasks("grunt-check-dependencies") |   grunt.loadNpmTasks("grunt-check-dependencies"); | ||||||
|   grunt.loadNpmTasks("grunt-eslint") |   grunt.loadNpmTasks("grunt-eslint"); | ||||||
| } | }; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue