Completed
Pull Request — develop (#168)
by
unknown
35s
created

node.js ➔ ... ➔ link.onclick   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
dl 0
loc 3
rs 10
nop 1
1
define(['sorttable', 'snabbdom', 'd3-interpolate', 'moment', 'helper'],
2
  function (SortTable, V, d3Interpolate, moment, helper) {
3
    'use strict';
4
    V = V.default;
5
6
    function showGeoURI(d) {
7
      if (!helper.hasLocation(d)) {
8
        return undefined;
9
      }
10
11
      return function (el) {
12
        var a = document.createElement('a');
13
        a.textContent = Number(d.nodeinfo.location.latitude.toFixed(6)) + ', ' + Number(d.nodeinfo.location.longitude.toFixed(6));
14
        a.href = 'geo:' + d.nodeinfo.location.latitude + ',' + d.nodeinfo.location.longitude;
15
        el.appendChild(a);
16
      };
17
    }
18
19
    function showStatus(d) {
20
      return function (el) {
21
        el.classList.add(d.flags.unseen ? 'unseen' : (d.flags.online ? 'online' : 'offline'));
22
        el.textContent = _.t((d.flags.online ? 'node.lastOnline' : 'node.lastOffline'), {
23
          time: d.lastseen.fromNow(),
24
          date: d.lastseen.format('DD.MM.YYYY, H:mm:ss')
25
        });
26
      };
27
    }
28
29
    function showFirmware(d) {
30
      return [
31
        helper.dictGet(d.nodeinfo, ['software', 'firmware', 'release']),
32
        helper.dictGet(d.nodeinfo, ['software', 'firmware', 'base'])
33
      ].filter(function (n) {
34
        return n !== null;
35
      }).join(' / ') || undefined;
36
    }
37
38
    function showSite(d, config) {
39
      var site = helper.dictGet(d.nodeinfo, ['system', 'site_code']);
40
      var rt = site;
41
      if (config.siteNames) {
42
        config.siteNames.forEach(function (t) {
43
          if (site === t.site) {
44
            rt = t.name;
45
          }
46
        });
47
      }
48
      return rt || undefined;
49
    }
50
51
    function showUptime(d) {
52
      if (!('uptime' in d.statistics)) {
53
        return undefined;
54
      }
55
56
      return moment.duration(d.statistics.uptime, 'seconds').humanize();
57
    }
58
59
    function showFirstseen(d) {
60
      if (!('firstseen' in d)) {
61
        return undefined;
62
      }
63
64
      return d.firstseen.fromNow(true);
65
    }
66
67
    function showWifiChannel(d, band) {
68
      if (!(d.nodeinfo.wireless) || !(band in d.nodeinfo.wireless)) {
69
        return undefined;
70
      }
71
72
      var channel = d.nodeinfo.wireless[band];
73
      var alias = wifiChannelAlias(channel);
74
75
      return d.nodeinfo.wireless[band] + ' (' + alias + ')';
76
    }
77
78
    function wifiChannelAlias(ch) {
79
      var chlist = {
80
        '1': '2412 MHz',
81
        '2': '2417 MHz',
82
        '3': '2422 MHz',
83
        '4': '2427 MHz',
84
        '5': '2432 MHz',
85
        '6': '2437 MHz',
86
        '7': '2442 MHz',
87
        '8': '2447 MHz',
88
        '9': '2452 MHz',
89
        '10': '2457 MHz',
90
        '11': '2462 MHz',
91
        '12': '2467 MHz',
92
        '13': '2472 MHz',
93
        '36': '5180 MHz (Indoors)',
94
        '40': '5200 MHz (Indoors)',
95
        '44': '5220 MHz (Indoors)',
96
        '48': '5240 MHz (Indoors)',
97
        '52': '5260 MHz (Indoors/DFS/TPC)',
98
        '56': '5280 MHz (Indoors/DFS/TPC)',
99
        '60': '5300 MHz (Indoors/DFS/TPC)',
100
        '64': '5320 MHz (Indoors/DFS/TPC)',
101
        '100': '5500 MHz (DFS) !!',
102
        '104': '5520 MHz (DFS) !!',
103
        '108': '5540 MHz (DFS) !!',
104
        '112': '5560 MHz (DFS) !!',
105
        '116': '5580 MHz (DFS) !!',
106
        '120': '5600 MHz (DFS) !!',
107
        '124': '5620 MHz (DFS) !!',
108
        '128': '5640 MHz (DFS) !!',
109
        '132': '5660 MHz (DFS) !!',
110
        '136': '5680 MHz (DFS) !!',
111
        '140': '5700 MHz (DFS) !!'
112
      };
113
      if (!(ch in chlist)) {
114
        return '';
115
      }
116
      return chlist[ch];
117
    }
118
119
    function showWifiAirtime(d, band) {
120
      if (!(d.statistics.wireless) || !(band in d.statistics.wireless)) {
121
        return undefined;
122
      }
123
124
      return function (el) {
125
        var value = Math.round(d.statistics.wireless[band] * 100) + ' %';
126
        var width = d.statistics.wireless[band];
127
        var warning = false;
128
        el.appendChild(showBar(value, width, warning));
129
      };
130
    }
131
132
    function showClients(d) {
133
      if (!d.flags.online) {
134
        return undefined;
135
      }
136
137
      var meshclients = getMeshClients(d);
138
      resetMeshClients(d);
139
      var before = '     (';
140
      var after = ' ' + _.t('clients_in_cloud') + ')';
141
142
143
      return function (el) {
144
        el.appendChild(document.createTextNode(d.statistics.clients > 0 ? d.statistics.clients : _.t('none')));
145
        if (meshclients > 0) {
146
          el.appendChild(document.createTextNode(before));
147
          el.appendChild(document.createTextNode(meshclients > 0 ? meshclients : _.t('none')));
148
          el.appendChild(document.createTextNode(after));
149
        }
150
        el.appendChild(document.createElement('br'));
151
152
        var span = document.createElement('span');
153
        span.classList.add('clients');
154
        span.innerHTML = '<i class="ion-person"></i>'.repeat(d.statistics.clients);
155
        el.appendChild(span);
156
157
        var spanmesh = document.createElement('span');
158
        spanmesh.classList.add('clientsMesh');
159
        spanmesh.innerHTML = '<i class="ion-person" style="opacity: .25;"></i>'.repeat(meshclients - d.statistics.clients);
160
        el.appendChild(spanmesh);
161
      };
162
    }
163
164
    function showIPs(d) {
165
      var ips = helper.dictGet(d.nodeinfo, ['network', 'addresses']);
166
      if (ips === null) {
167
        return undefined;
168
      }
169
170
      ips.sort();
171
172
      return function (el) {
173
        ips.forEach(function (ip, i) {
174
          var link = !ip.startsWith('fe80:');
175
176
          if (i > 0) {
177
            el.appendChild(document.createElement('br'));
178
          }
179
180
          if (link) {
181
            var a = document.createElement('a');
182
            a.href = 'http://[' + ip + ']/';
183
            a.textContent = ip;
184
            el.appendChild(a);
185
          } else {
186
            el.appendChild(document.createTextNode(ip));
187
          }
188
        });
189
      };
190
    }
191
192
    function showBar(v, width, warning) {
193
      var span = document.createElement('span');
194
      span.classList.add('bar');
195
196
      var bar = document.createElement('span');
197
      bar.style.width = (width * 100) + '%';
198
      if (warning) {
199
        span.classList.add('warning');
200
      }
201
      span.appendChild(bar);
202
203
      var label = document.createElement('label');
204
      label.textContent = v;
205
      span.appendChild(label);
206
207
      return span;
208
    }
209
210
    function showLoad(d) {
211
      if (!('loadavg' in d.statistics)) {
212
        return undefined;
213
      }
214
215
      return function (el) {
216
        var value = d.statistics.loadavg.toFixed(2);
217
        var width = d.statistics.loadavg % 1;
218
        var warning = false;
219
        if (d.statistics.loadavg >= d.nodeinfo.hardware.nproc) {
220
          warning = true;
221
        }
222
        el.appendChild(showBar(value, width, warning));
223
      };
224
    }
225
226
    function showRAM(d) {
227
      if (!('memory_usage' in d.statistics)) {
228
        return undefined;
229
      }
230
231
      return function (el) {
232
        var value = Math.round(d.statistics.memory_usage * 100) + ' %';
233
        var width = d.statistics.memory_usage;
234
        var warning = false;
235
        if (d.statistics.memory_usage >= 0.8) {
236
          warning = true;
237
        }
238
        el.appendChild(showBar(value, width, warning));
239
      };
240
    }
241
242
    function showAutoupdate(d) {
243
      var au = helper.dictGet(d.nodeinfo, ['software', 'autoupdater']);
244
      if (!au) {
245
        return undefined;
246
      }
247
248
      return au.enabled ? _.t('node.activated', { branch: au.branch }) : _.t('node.deactivated');
249
    }
250
251
    function showUplink(d) {
252
      if (d.nodeinfo.vpn) {
253
        return undefined;
254
      }
255
      var hasUplink = d.flags.uplink ? d.flags.uplink : false;
256
      return hasUplink ? _.t('yes') : _.t('no');
257
    }
258
259
    function showStatImg(o, d) {
260
      var subst = {};
261
      subst['{NODE_ID}'] = d.nodeinfo.node_id;
262
      subst['{NODE_NAME}'] = d.nodeinfo.hostname.replace(/[^a-z0-9\-]/ig, '_');
263
      subst['{TIME}'] = d.lastseen.format('DDMMYYYYHmmss');
264
      subst['{LOCALE}'] = _.locale();
265
      return helper.showStat(o, subst);
266
    }
267
268
    function getMeshClients(node) {
269
      var meshclients = 0;
270
      if (node.statistics && !isNaN(node.statistics.clients)) {
271
        meshclients = node.statistics.clients;
272
      }
273
274
      if (!node) {
275
        return 0;
276
      }
277
278
      if (node.parsed) {
279
        return 0;
280
      }
281
282
      node.parsed = 1;
283
      node.neighbours.forEach(function (neighbour) {
284
        if (!neighbour.link.isVPN && neighbour.node) {
285
          meshclients += getMeshClients(neighbour.node);
286
        }
287
      });
288
289
      return meshclients;
290
    }
291
292
    function resetMeshClients(node) {
293
      if (!node.parsed) {
294
        return;
295
      }
296
297
      node.parsed = 0;
298
299
      node.neighbours.forEach(function (neighbour) {
300
        if (!neighbour.link.isVPN && neighbour.node) {
301
          resetMeshClients(neighbour.node);
302
        }
303
      });
304
305
      return;
0 ignored issues
show
Unused Code introduced by
This return has no effect and can be removed.
Loading history...
306
    }
307
308
    function createLink(target, router) {
309
      if (!target) {
310
        return document.createTextNode(_.t('unkown'));
311
      }
312
313
      var text = target.nodeinfo ? target.nodeinfo.hostname : target;
314
      if (target.nodeinfo) {
315
        var link = document.createElement('a');
316
        link.classList.add('hostname-lin');
317
        link.href = router.generateLink({ node: target.nodeinfo.node_id });
318
        link.onclick = function (e) {
319
          router.fullUrl({ node: target.nodeinfo.node_id }, e);
320
        };
321
        link.textContent = text;
322
        return link;
323
      }
324
      return document.createTextNode(text);
325
    }
326
327
    function lookupNode(d, nodeDict) {
328
      if (!d) {
329
        return null;
330
      }
331
332
      var node = nodeDict[d.substr(0, 17)];
333
      if (!node) {
334
        return d;
335
      }
336
      return node;
337
    }
338
339
    function showGateway(d, router, nodeDict) {
340
      var nh = null;
341
      if (helper.dictGet(d.statistics, ['nexthop'])) {
342
        nh = helper.dictGet(d.statistics, ['nexthop']);
343
      }
344
      if (helper.dictGet(d.statistics, ['gateway_nexthop'])) {
345
        nh = helper.dictGet(d.statistics, ['gateway_nexthop']);
346
      }
347
      var gw = helper.dictGet(d.statistics, ['gateway']);
348
349
      if (!gw) {
350
        return null;
351
      }
352
      return function (el) {
353
        if (nh) {
354
          el.appendChild(createLink(lookupNode(nh, nodeDict), router));
355
          if (nh.substr(0, 17) !== gw.substr(0, 17)) {
356
            el.appendChild(document.createTextNode(' -> ... -> '));
357
          }
358
        }
359
        if (!nh || nh.substr(0, 17) !== gw.substr(0, 17)) {
360
          el.appendChild(createLink(lookupNode(gw, nodeDict), router));
361
        }
362
      };
363
    }
364
365
    return function (config, el, router, d, gateways) {
366
      var linkScale = d3Interpolate.interpolate('#F02311', '#04C714');
367
368
      function renderNeighbourRow(n) {
369
        var icons = [];
370
        icons.push(V.h('span', { props: { className: n.incoming ? 'ion-arrow-left-c' : 'ion-arrow-right-c' } }));
371
        if (helper.hasLocation(n.node)) {
372
          icons.push(V.h('span', { props: { className: 'ion-location' } }));
373
        }
374
375
        var name = V.h('a', {
376
          props: {
377
            className: 'online',
378
            href: router.generateLink({ node: n.node.nodeinfo.node_id })
379
          }, on: {
380
            click: function (e) {
381
              router.fullUrl({ node: n.node.nodeinfo.node_id }, e);
382
            }
383
          }
384
        }, n.node.nodeinfo.hostname);
385
386
        var td1 = V.h('td', icons);
387
        var td2 = V.h('td', name);
388
        var td3 = V.h('td', (n.node.statistics.clients ? n.node.statistics.clients.toString() : '0'));
389
        var td4 = V.h('td', { style: { color: linkScale(1 / n.link.tq) } }, helper.showTq(n.link));
390
        var td5 = V.h('td', helper.showDistance(n.link));
391
392
        return V.h('tr', [td1, td2, td3, td4, td5]);
393
      }
394
395
      var h2 = document.createElement('h2');
396
      h2.textContent = d.nodeinfo.hostname;
397
      el.appendChild(h2);
398
399
      var attributes = document.createElement('table');
400
      attributes.classList.add('attributes');
401
402
      helper.attributeEntry(attributes, 'node.status', showStatus(d));
403
      helper.attributeEntry(attributes, 'node.gateway', d.flags.gateway ? 'ja' : null);
404
      helper.attributeEntry(attributes, 'node.coordinates', showGeoURI(d));
405
406
      if (config.nodeInfobox && config.nodeInfobox.contact) {
407
        helper.attributeEntry(attributes, 'node.contact', helper.dictGet(d.nodeinfo, ['owner', 'contact']));
408
      }
409
410
      helper.attributeEntry(attributes, 'node.hardware', helper.dictGet(d.nodeinfo, ['hardware', 'model']));
411
      helper.attributeEntry(attributes, 'node.primaryMac', helper.dictGet(d.nodeinfo, ['network', 'mac']));
412
      helper.attributeEntry(attributes, 'node.id', helper.dictGet(d.nodeinfo, ['node_id']));
413
      helper.attributeEntry(attributes, 'node.firmware', showFirmware(d));
414
      helper.attributeEntry(attributes, 'node.site', showSite(d, config));
415
      helper.attributeEntry(attributes, 'node.uptime', showUptime(d));
416
      helper.attributeEntry(attributes, 'node.firstSeen', showFirstseen(d));
417
418
      helper.attributeEntry(attributes, 'node.wifi_channel_2', showWifiChannel(d, 'chan2'));
419
      helper.attributeEntry(attributes, 'node.wifi_channel_5', showWifiChannel(d, 'chan5'));
420
      helper.attributeEntry(attributes, 'node.wifi_airtime_2', showWifiAirtime(d, 'airtime2'));
421
      helper.attributeEntry(attributes, 'node.wifi_airtime_5', showWifiAirtime(d, 'airtime5'));
422
423
424
      if (config.nodeInfobox && config.nodeInfobox.hardwareUsage) {
425
        helper.attributeEntry(attributes, 'node.systemLoad', showLoad(d));
426
        helper.attributeEntry(attributes, 'node.ram', showRAM(d));
427
      }
428
      helper.attributeEntry(attributes, 'node.ipAddresses', showIPs(d));
429
      // helper.attributeEntry(attributes, 'node.selectedGateway', gateways[helper.dictGet(d.statistics, ['gateway'])]);
430
      helper.attributeEntry(attributes, 'node.selectedGateway', showGateway(d, router, gateways));
431
      helper.attributeEntry(attributes, 'node.update', showAutoupdate(d));
432
      helper.attributeEntry(attributes, 'node.uplink', showUplink(d));
433
      helper.attributeEntry(attributes, 'node.clients', showClients(d));
434
435
      el.appendChild(attributes);
436
437
      if (d.neighbours.length > 0) {
438
        var h3 = document.createElement('h3');
439
        h3.textContent = _.t('node.link', d.neighbours.length) + ' (' + d.neighbours.length + ')';
440
        el.appendChild(h3);
441
442
        var headings = [{
443
          name: ''
444
        }, {
445
          name: 'node.nodes',
446
          sort: function (a, b) {
447
            return a.node.nodeinfo.hostname.localeCompare(b.node.nodeinfo.hostname);
448
          },
449
          reverse: false
450
        }, {
451
          name: 'node.clients',
452
          class: 'ion-people',
453
          sort: function (a, b) {
454
            return ('clients' in a.node.statistics ? a.node.statistics.clients : -1) -
455
              ('clients' in b.node.statistics ? b.node.statistics.clients : -1);
456
          },
457
          reverse: true
458
        }, {
459
          name: 'node.tq',
460
          class: 'ion-connection-bars',
461
          sort: function (a, b) {
462
            return a.link.tq - b.link.tq;
463
          },
464
          reverse: true
465
        }, {
466
          name: 'node.distance',
467
          class: 'ion-arrow-resize',
468
          sort: function (a, b) {
469
            return (a.link.distance === undefined ? -1 : a.link.distance) -
470
              (b.link.distance === undefined ? -1 : b.link.distance);
471
          },
472
          reverse: true
473
        }];
474
475
        var table = new SortTable(headings, 1, renderNeighbourRow);
476
        table.setData(d.neighbours);
477
        table.el.elm.classList.add('node-links');
478
        el.appendChild(table.el.elm);
479
      }
480
481
      if (config.nodeInfos) {
482
        config.nodeInfos.forEach(function (nodeInfo) {
483
          var h4 = document.createElement('h4');
484
          h4.textContent = nodeInfo.name;
485
          el.appendChild(h4);
486
          el.appendChild(showStatImg(nodeInfo, d));
487
        });
488
      }
489
    };
490
  });
491