1
+ <!DOCTYPE html>
2
+ < html lang ="en ">
3
+ < head >
4
+ < meta charset ="utf-8 ">
5
+ < meta name ="viewport " content ="width=device-width, initial-scale=1 ">
6
+ < link rel ="icon " type ="image/svg " href ="https://files.oxl.at/img/oxl3_sm.png ">
7
+
8
+ < script src ="https://d3js.org/d3.v4.min.js "> </ script >
9
+ < style >
10
+ # chart {
11
+ width : 100vw ;
12
+ height : 100vh ;
13
+ }
14
+
15
+ .net {
16
+ overflow : hidden;
17
+ font-weight : bold;
18
+ transform
19
+ }
20
+
21
+ .net-cidr {
22
+ display : inline-block;
23
+ margin-top : 100px ;
24
+ }
25
+
26
+ .net-info {
27
+ visibility : hidden;
28
+ display : none;
29
+ }
30
+
31
+ # infos {
32
+ width : 20vw ;
33
+ height : 25vh ;
34
+ background-color : whitesmoke;
35
+ opacity : 0.85 ;
36
+ z-index : 2 ;
37
+ border-radius : 10px ;
38
+ border-width : 2px ;
39
+ border-color : black;
40
+ border-style : solid;
41
+ position : absolute;
42
+ padding : 20px ;
43
+ overflow : hidden;
44
+ }
45
+
46
+ </ style >
47
+ </ head >
48
+ < body >
49
+ <!--
50
+ see: https://observablehq.com/@d3/treemap/2
51
+ see2: https://d3-graph-gallery.com/graph/treemap_basic.html
52
+ -->
53
+ < div id ="infos "> </ div >
54
+ < svg id ="chart "> </ svg >
55
+ < script >
56
+ // start test webserver: 'python3 test_server.py'
57
+
58
+ const WIDTH = window . innerWidth ;
59
+ const HEIGHT = window . innerHeight - 50 ;
60
+ const NET_PAD = 5 ;
61
+ const CATEGORIES = [ 'all' , 'bot' , 'probe' , 'rate' , 'attack' , 'crawler' ] ;
62
+ const COLORSCHEME = [
63
+ // red-ish
64
+ "#B93022" , "#FF383D" , "#D32669" , "#C43F03" , "#E70291" , "#E211C0" , "#6A3D52" , "#C75839" , "#E7A8B0" ,
65
+ "#DB7277" , "#A23E48" ,
66
+ // purple-ish
67
+ "#9F8AD2" , "#4B2ED1" , "#9C1091" , "#EA77DF" , "#8D0BE8" , "#BB65BC" , "#993CF3" , "#9F09EF" , "#CD0099" ,
68
+ "#EA2794" , "#DC8581" , "#9C1686" ,
69
+ // orange-ish
70
+ "#E37F6F" , "#C5824E" , "#FDCA29" , "#D99620" , "#E7C014" , "#F9C630" , "#F6BC55" , "#B9AB58" , "#DB8E94" ,
71
+ "#D27E7E" ,
72
+ ] ;
73
+
74
+ function numberWithDot ( x ) {
75
+ return x . toString ( ) . replace ( / \B (? = ( \d { 3 } ) + (? ! \d ) ) / g, "." ) ;
76
+ }
77
+
78
+ function showNetInfos ( e ) {
79
+ let infos = JSON . parse ( e . currentTarget . querySelectorAll ( ".net-info" ) [ 0 ] . textContent ) ;
80
+
81
+ var x = event . clientX ;
82
+ var y = event . clientY ;
83
+ console . log ( infos , x , y ) ;
84
+
85
+ var infoContainer = document . getElementById ( 'infos' ) ;
86
+ infoContainer . style . left = `${ x } px` ;
87
+ infoContainer . style . top = `${ y } px` ;
88
+
89
+ infoContainer . innerHTML = `<b>Network</b>: ${ infos . name } <br>` ;
90
+ infoContainer . innerHTML += `<b>Reported IPs</b>: ${ infos . reported_ips } <br>` ;
91
+ infoContainer . innerHTML += `<b>Reputation</b>: ${ infos . reputation . toUpperCase ( ) } <br>` ;
92
+ infoContainer . innerHTML += `<b>ASN</b>: ${ infos . asn } <br>` ;
93
+ infoContainer . innerHTML += `<b>Org</b>: ${ infos . as_name } <br>` ;
94
+
95
+ if ( infos . country !== undefined ) {
96
+ infoContainer . innerHTML += `<b>Country</b>: ${ infos . country } <br>` ;
97
+ }
98
+
99
+ let urls = [ ] ;
100
+ for ( let [ k , v ] of Object . entries ( infos . url ) ) {
101
+ urls . push ( `<a href="${ v } ">${ k } </a>` ) ;
102
+ }
103
+ infoContainer . innerHTML += `<b>URLs</b>: ${ urls . join ( ', ' ) } <br><hr>` ;
104
+
105
+ for ( let c of CATEGORIES ) {
106
+ if ( c in infos ) {
107
+ infoContainer . innerHTML += `<small><b>Reports ${ c } </b>: ${ numberWithDot ( infos [ c ] ) } </small><br>` ;
108
+ }
109
+ }
110
+ }
111
+
112
+ function createChart ( ) {
113
+ d3 . json ( 'net_tree.json' , function ( data ) {
114
+ var svg = d3 . select ( "#chart" ) ;
115
+
116
+ var root = d3 . stratify ( )
117
+ . id ( function ( d ) { return d . name ; } )
118
+ . parentId ( function ( d ) { if ( d . name == 'root' ) { return null } else { return 'root' } ; } )
119
+ ( data . children ) ;
120
+
121
+ root . sum ( function ( d ) { return + d . all } )
122
+
123
+ d3 . treemap ( )
124
+ . size ( [ WIDTH , HEIGHT ] )
125
+ . padding ( 4 )
126
+ ( root )
127
+
128
+ const colorScale = d3 . scaleOrdinal ( )
129
+ . range ( COLORSCHEME ) ;
130
+
131
+ const nets = svg . selectAll ( ".net" )
132
+ . data ( root . descendants ( ) . slice ( 1 ) )
133
+ . enter ( )
134
+ . append ( "g" )
135
+ . attr ( "class" , "net" ) ;
136
+
137
+ nets . append ( "rect" )
138
+ . attr ( 'x' , function ( d ) { return d . x0 ; } )
139
+ . attr ( 'y' , function ( d ) { return d . y0 ; } )
140
+ . attr ( 'width' , function ( d ) { return d . x1 - d . x0 ; } )
141
+ . attr ( 'height' , function ( d ) { return d . y1 - d . y0 ; } )
142
+ . style ( "fill" , d => colorScale ( d . data . reported_ips ) )
143
+ . attr ( "class" , "net" ) ;
144
+
145
+ nets . append ( "text" )
146
+ . append ( "tspan" )
147
+ . attr ( "class" , "net-cidr" )
148
+ . text ( d => d . data . name )
149
+ . style ( 'fill' , 'whitesmoke' ) ;
150
+
151
+ // hover infos
152
+ nets . append ( "text" )
153
+ . style ( "text-anchor" , "bottom" )
154
+ . append ( "tspan" )
155
+ . attr ( "class" , "net-info" )
156
+ . text ( d => JSON . stringify ( d . data ) ) ;
157
+
158
+ for ( let n of document . querySelectorAll ( ".net" ) ) {
159
+ let r = n . querySelectorAll ( "rect" ) [ 0 ] ;
160
+ if ( r === undefined ) {
161
+ continue ;
162
+ }
163
+ let c = n . querySelectorAll ( "text" ) [ 0 ] ;
164
+ c . setAttribute ( "transform" , `translate(${ r . x . baseVal . value + NET_PAD } , ${ r . y . baseVal . value + NET_PAD + 30 } )` ) ;
165
+
166
+ n . addEventListener ( "mouseover" , showNetInfos ) ;
167
+ }
168
+
169
+ } ) ;
170
+ }
171
+
172
+ createChart ( ) ;
173
+ </ script >
174
+ </ body >
175
+ </ html >
0 commit comments