Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / ot-br-posix / repo / src / web / web-service / frontend / res / js / app.js
1 /*
2  *    Copyright (c) 2017, The OpenThread Authors.
3  *    All rights reserved.
4  *
5  *    Redistribution and use in source and binary forms, with or without
6  *    modification, are permitted provided that the following conditions are met:
7  *    1. Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *    2. Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *    3. Neither the name of the copyright holder nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *    POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 (function() {
30     angular
31         .module('StarterApp', ['ngMaterial', 'ngMessages'])
32         .controller('AppCtrl', AppCtrl)
33         .service('sharedProperties', function() {
34             var index = 0;
35             var networkInfo;
36
37             return {
38                 getIndex: function() {
39                     return index;
40                 },
41                 setIndex: function(value) {
42                     index = value;
43                 },
44                 getNetworkInfo: function() {
45                     return networkInfo;
46                 },
47                 setNetworkInfo: function(value) {
48                     networkInfo = value
49                 },
50             };
51         });
52
53     function AppCtrl($scope, $http, $mdDialog, $interval, sharedProperties) {
54         $scope.menu = [{
55                 title: 'Home',
56                 icon: 'home',
57                 show: true,
58             },
59             {
60                 title: 'Join',
61                 icon: 'add_circle_outline',
62                 show: false,
63             },
64             {
65                 title: 'Form',
66                 icon: 'open_in_new',
67                 show: false,
68             },
69             {
70                 title: 'Status',
71                 icon: 'info_outline',
72                 show: false,
73             },
74             {
75                 title: 'Settings',
76                 icon: 'settings',
77                 show: false,
78             },
79             {
80                 title: 'Commission',
81                 icon: 'add_circle_outline',
82                 show: false,
83             },
84             {
85                 title: 'Topology',
86                 icon: 'add_circle_outline',
87                 show: false,
88             },
89
90         ];
91
92         $scope.thread = {
93             networkName: 'OpenThreadDemo',
94             extPanId: '1111111122222222',
95             panId: '0x1234',
96             passphrase: '123456',
97             masterKey: '00112233445566778899aabbccddeeff',
98             channel: 15,
99             prefix: 'fd11:22::',
100             defaultRoute: true,
101         };
102
103         $scope.setting = {
104             prefix: 'fd11:22::',
105             defaultRoute: true,
106         };
107
108         $scope.headerTitle = 'Home';
109         $scope.status = [];
110
111         $scope.isLoading = false;
112
113         $scope.showScanAlert = function(ev) {
114             $mdDialog.show(
115                 $mdDialog.alert()
116                 .parent(angular.element(document.querySelector('#popupContainer')))
117                 .clickOutsideToClose(true)
118                 .title('Information')
119                 .textContent('There is no available Thread network currently, please \
120                              wait a moment and retry it.')
121                 .ariaLabel('Alert Dialog Demo')
122                 .ok('Okay')
123             );
124         };
125         $scope.showPanels = function(index) {
126             $scope.headerTitle = $scope.menu[index].title;
127             for (var i = 0; i < 7; i++) {
128                 $scope.menu[i].show = false;
129             }
130             $scope.menu[index].show = true;
131             if (index == 1) {
132                 $scope.isLoading = true;
133                 $http.get('/available_network').then(function(response) {
134                     $scope.isLoading = false;
135                     if (response.data.error == 0) {
136                         $scope.networksInfo = response.data.result;
137                     } else {
138                         $scope.showScanAlert(event);
139                     }
140                 });
141             }
142             if (index == 3) {
143                 $http.get('/get_properties').then(function(response) {
144                     console.log(response);
145                     if (response.data.error == 0) {
146                         var statusJson = response.data.result;
147                         $scope.status = [];
148                         for (var i = 0; i < Object.keys(statusJson).length; i++) {
149                             $scope.status.push({
150                                 name: Object.keys(statusJson)[i],
151                                 value: statusJson[Object.keys(statusJson)[i]],
152                                 icon: 'res/img/icon-info.png',
153                             });
154                         }
155                     }
156                 });
157             }
158             if (index == 6) {
159                 $scope.dataInit();
160                 $scope.showTopology();
161             }
162         };
163
164         $scope.showJoinDialog = function(ev, index, item) {
165             sharedProperties.setIndex(index);
166             sharedProperties.setNetworkInfo(item);
167             $scope.index = index;
168             $mdDialog.show({
169                 controller: DialogController,
170                 templateUrl: 'join.dialog.html',
171                 parent: angular.element(document.body),
172                 targetEvent: ev,
173                 clickOutsideToClose: true,
174                 fullscreen: $scope.customFullscreen,
175             });
176         };
177
178         function DialogController($scope, $mdDialog, $http, $interval, sharedProperties) {
179             var index = sharedProperties.getIndex();
180             $scope.isDisplay = false;
181             $scope.thread = {
182                 masterKey: '00112233445566778899aabbccddeeff',
183                 prefix: 'fd11:22::',
184                 defaultRoute: true,
185             };
186
187             $scope.showAlert = function(ev, result) {
188                 $mdDialog.show(
189                     $mdDialog.alert()
190                     .parent(angular.element(document.querySelector('#popupContainer')))
191                     .clickOutsideToClose(true)
192                     .title('Information')
193                     .textContent('Join operation is ' + result)
194                     .ariaLabel('Alert Dialog Demo')
195                     .ok('Okay')
196                     .targetEvent(ev)
197                 );
198             };
199
200             $scope.join = function(valid) {
201                 if (!valid)
202                 {
203                     return;
204                 }
205
206                 if ($scope.thread.defaultRoute == null) {
207                     $scope.thread.defaultRoute = false;
208                 };
209                 $scope.isDisplay = true;
210                 var data = {
211                     masterKey: $scope.thread.masterKey,
212                     prefix: $scope.thread.prefix,
213                     defaultRoute: $scope.thread.defaultRoute,
214                     index: index,
215                 };
216                 var httpRequest = $http({
217                     method: 'POST',
218                     url: '/join_network',
219                     data: data,
220                 });
221
222                 httpRequest.then(function successCallback(response) {
223                     $scope.res = response.data.result;
224                     if (response.data.result == 'successful') {
225                         $mdDialog.hide();
226                     }
227                     $scope.isDisplay = false;
228                     $scope.showAlert(event, response.data.result);
229                 });
230             };
231
232             $scope.cancel = function() {
233                 $mdDialog.cancel();
234             };
235         };
236
237
238         $scope.showConfirm = function(ev, valid) {
239             if (!valid)
240             {
241                 return;
242             }
243
244             var confirm = $mdDialog.confirm()
245                 .title('Are you sure you want to Form the Thread Network?')
246                 .textContent('')
247                 .targetEvent(ev)
248                 .ok('Okay')
249                 .cancel('Cancel');
250
251             $mdDialog.show(confirm).then(function() {
252                 if ($scope.thread.defaultRoute == null) {
253                     $scope.thread.defaultRoute = false;
254                 };
255                 var data = {
256                     masterKey: $scope.thread.masterKey,
257                     prefix: $scope.thread.prefix,
258                     defaultRoute: $scope.thread.defaultRoute,
259                     extPanId: $scope.thread.extPanId,
260                     panId: $scope.thread.panId,
261                     passphrase: $scope.thread.passphrase,
262                     channel: $scope.thread.channel,
263                     networkName: $scope.thread.networkName,
264                 };
265                 $scope.isForming = true;
266                 var httpRequest = $http({
267                     method: 'POST',
268                     url: '/form_network',
269                     data: data,
270                 });
271
272                 httpRequest.then(function successCallback(response) {
273                     $scope.res = response.data.result;
274                     if (response.data.result == 'successful') {
275                         $mdDialog.hide();
276                     }
277                     $scope.isForming = false;
278                     $scope.showAlert(event, 'FORM', response.data.result);
279                 });
280             }, function() {
281                 $mdDialog.cancel();
282             });
283         };
284
285         $scope.showAlert = function(ev, operation, result) {
286             $mdDialog.show(
287                 $mdDialog.alert()
288                 .parent(angular.element(document.querySelector('#popupContainer')))
289                 .clickOutsideToClose(true)
290                 .title('Information')
291                 .textContent(operation + ' operation is ' + result)
292                 .ariaLabel('Alert Dialog Demo')
293                 .ok('Okay')
294                 .targetEvent(ev)
295             );
296         };
297
298         $scope.showAddConfirm = function(ev) {
299             var confirm = $mdDialog.confirm()
300                 .title('Are you sure you want to Add this On-Mesh Prefix?')
301                 .textContent('')
302                 .targetEvent(ev)
303                 .ok('Okay')
304                 .cancel('Cancel');
305
306             $mdDialog.show(confirm).then(function() {
307                 if ($scope.setting.defaultRoute == null) {
308                     $scope.setting.defaultRoute = false;
309                 };
310                 var data = {
311                     prefix: $scope.setting.prefix,
312                     defaultRoute: $scope.setting.defaultRoute,
313                 };
314                 var httpRequest = $http({
315                     method: 'POST',
316                     url: '/add_prefix',
317                     data: data,
318                 });
319
320                 httpRequest.then(function successCallback(response) {
321                     $scope.showAlert(event, 'Add', response.data.result);
322                 });
323             }, function() {
324                 $mdDialog.cancel();
325             });
326         };
327
328         $scope.showDeleteConfirm = function(ev) {
329             var confirm = $mdDialog.confirm()
330                 .title('Are you sure you want to Delete this On-Mesh Prefix?')
331                 .textContent('')
332                 .targetEvent(ev)
333                 .ok('Okay')
334                 .cancel('Cancel');
335
336             $mdDialog.show(confirm).then(function() {
337                 var data = {
338                     prefix: $scope.setting.prefix,
339                 };
340                 var httpRequest = $http({
341                     method: 'POST',
342                     url: '/delete_prefix',
343                     data: data,
344                 });
345
346                 httpRequest.then(function successCallback(response) {
347                     $scope.showAlert(event, 'Delete', response.data.result);
348                 });
349             }, function() {
350                 $mdDialog.cancel();
351             });
352         };
353
354         $scope.startCommission = function(ev) {
355             var data = {
356                 pskd: $scope.commission.pskd,
357                 passphrase: $scope.commission.passphrase,
358             };
359             var httpRequest = $http({
360                 method: 'POST',
361                 url: '/commission',
362                 data: data,
363             });
364             
365             ev.target.disabled = true;
366             
367             httpRequest.then(function successCallback(response) {
368                 if (response.data.error == 0) {
369                     $scope.showAlert(event, 'Commission', 'success');
370                 } else {
371                     $scope.showAlert(event, 'Commission', 'failed');
372                 }
373                 ev.target.disabled = false;
374             });
375         };
376
377         $scope.restServerPort = '8081';
378         $scope.ipAddr = window.location.hostname + ':' + $scope.restServerPort;
379
380         // Basic information line
381         $scope.basicInfo = {
382             'NetworkName' : 'Unknown',
383             'LeaderData'  :{'LeaderRouterId' : 'Unknown'}
384         }
385         // Num of router calculated by diagnostic
386         $scope.NumOfRouter = 'Unknown';
387
388         // Diagnostic information for detailed display
389         $scope.nodeDetailInfo = 'Unknown';
390         // For response of Diagnostic
391         $scope.networksDiagInfo = '';
392         $scope.graphisReady = false;
393         $scope.detailList = {
394             'ExtAddress': { 'title': false, 'content': true },
395             'Rloc16': { 'title': false, 'content': true },
396             'Mode': { 'title': false, 'content': false },
397             'Connectivity': { 'title': false, 'content': false },
398             'Route': { 'title': false, 'content': false },
399             'LeaderData': { 'title': false, 'content': false },
400             'NetworkData': { 'title': false, 'content': true },
401             'IP6Address List': { 'title': false, 'content': true },
402             'MACCounters': { 'title': false, 'content': false },
403             'ChildTable': { 'title': false, 'content': false },
404             'ChannelPages': { 'title': false, 'content': false }
405         };
406         $scope.graphInfo = {
407             'nodes': [],
408             'links': []
409         }
410
411         $scope.dataInit = function() {
412
413             $http.get('http://' + $scope.ipAddr + '/node').then(function(response) {
414
415                 $scope.basicInfo = response.data;
416                 console.log(response.data);
417                 $scope.basicInfo.Rloc16 = $scope.intToHexString($scope.basicInfo.Rloc16,4);
418                 $scope.basicInfo.LeaderData.LeaderRouterId = '0x' + $scope.intToHexString($scope.basicInfo.LeaderData.LeaderRouterId,2);
419             });
420         }
421         $scope.isObject = function(obj) {
422             return obj.constructor === Object;
423         }
424         $scope.isArray = function(arr) {
425             return !!arr && arr.constructor === Array;
426         }
427
428         $scope.clickList = function(key) {
429             $scope.detailList[key]['content'] = !$scope.detailList[key]['content']
430         }
431
432         $scope.intToHexString = function(num, len){
433             var value;
434             value  = num.toString(16);
435             
436             while( value.length < len ){
437                 value = '0' + value;
438             }
439             return value;
440         }
441         $scope.showTopology = function() {
442             var nodeMap = {}
443             var count, src, dist, rloc, child, rlocOfParent, rlocOfChild, diagOfNode, linkNode, childInfo;
444
445             $scope.graphisReady = false;
446             $scope.graphInfo = {
447                 'nodes': [],
448                 'links': []
449             };
450             $http.get('http://' + $scope.ipAddr + '/diagnostics').then(function(response) {
451
452                 
453                 $scope.networksDiagInfo = response.data;
454                 for (diagOfNode of $scope.networksDiagInfo){
455                     
456                     diagOfNode['RouteId'] = '0x' + $scope.intToHexString(diagOfNode['Rloc16'] >> 10,2);
457                     
458                     diagOfNode['Rloc16'] = '0x' + $scope.intToHexString(diagOfNode['Rloc16'],4);
459                     
460                     diagOfNode['LeaderData']['LeaderRouterId'] = '0x' + $scope.intToHexString(diagOfNode['LeaderData']['LeaderRouterId'],2);
461                     for (linkNode of diagOfNode['Route']['RouteData']){
462                         linkNode['RouteId'] = '0x' + $scope.intToHexString(linkNode['RouteId'],2);
463                     }
464                 }
465                 
466                 count = 0;
467                 
468                 for (diagOfNode of $scope.networksDiagInfo) {
469                     if ('ChildTable' in diagOfNode) {
470                         
471                         rloc = parseInt(diagOfNode['Rloc16'],16).toString(16);
472                         nodeMap[rloc] = count;
473                         
474                         if ( diagOfNode['RouteId'] == diagOfNode['LeaderData']['LeaderRouterId']) {
475                             diagOfNode['Role'] = 'Leader';
476                         } else {
477                             diagOfNode['Role'] = 'Router';
478                         }
479
480                         $scope.graphInfo.nodes.push(diagOfNode);
481                         
482                         if (diagOfNode['Rloc16'] === $scope.basicInfo.rloc16) {
483                             $scope.nodeDetailInfo = diagOfNode
484                         }
485                         count = count + 1;
486                     }
487                 }
488                 // Num of Router is based on the diagnostic information
489                 $scope.NumOfRouter = count;
490                 
491                 // Index for a second loop
492                 src = 0;
493                 // Construct links 
494                 for (diagOfNode of $scope.networksDiagInfo) {
495                     if ('ChildTable' in diagOfNode) {
496                         // Link bewtwen routers
497                         for (linkNode of diagOfNode['Route']['RouteData']) {
498                             rloc = ( parseInt(linkNode['RouteId'],16) << 10).toString(16);
499                             if (rloc in nodeMap) {
500                                 dist = nodeMap[rloc];
501                                 if (src < dist) {
502                                     $scope.graphInfo.links.push({
503                                         'source': src,
504                                         'target': dist,
505                                         'weight': 1,
506                                         'type': 0,
507                                         'linkInfo': {
508                                             'inQuality': linkNode['LinkQualityIn'],
509                                             'outQuality': linkNode['LinkQualityOut']
510                                         }
511                                     });
512                                 }
513                             }
514                         }
515
516                         // Link between router and child 
517                         for (childInfo of diagOfNode['ChildTable']) {
518                             child = {};
519                             rlocOfParent = parseInt(diagOfNode['Rloc16'],16).toString(16);
520                             rlocOfChild = (parseInt(diagOfNode['Rloc16'],16) + childInfo['ChildId']).toString(16);
521
522                             src = nodeMap[rlocOfParent];
523                             
524                             child['Rloc16'] = '0x' + rlocOfChild;
525                             child['RouteId'] = diagOfNode['RouteId'];
526                             nodeMap[rlocOfChild] = count;
527                             child['Role'] = 'Child';
528                             $scope.graphInfo.nodes.push(child);
529                             $scope.graphInfo.links.push({
530                                 'source': src,
531                                 'target': count,
532                                 'weight': 1,
533                                 'type': 1,
534                                 'linkInfo': {
535                                     'Timeout': childInfo['Timeout'],
536                                     'Mode': childInfo['Mode']
537                                 }
538
539                             });
540
541                             count = count + 1;
542                         }
543                     }
544                     src = src + 1;
545                 }
546                
547                 $scope.drawGraph();
548             })
549         }
550
551         
552         $scope.updateDetailLabel = function() {
553             for (var detailInfoKey in $scope.detailList) {
554                 $scope.detailList[detailInfoKey]['title'] = false;
555             }
556             for (var diagInfoKey in $scope.nodeDetailInfo) {
557                 if (diagInfoKey in $scope.detailList) {
558                     $scope.detailList[diagInfoKey]['title'] = true;
559                 }
560
561             }
562         }
563
564         
565         $scope.drawGraph = function() {
566             var json, svg, tooltip, force;
567             var scale, len;
568
569             document.getElementById('topograph').innerHTML = '';
570             scale = $scope.graphInfo.nodes.length;
571             len = 125 * Math.sqrt(scale);
572
573             // Topology graph
574             svg = d3.select('.d3graph').append('svg')
575                 .attr('preserveAspectRatio', 'xMidYMid meet')
576                 .attr('viewBox', '0, 0, ' + len.toString(10) + ', ' + (len / (3 / 2)).toString(10));
577             
578             // Legend
579             svg.append('circle')
580                 .attr('cx',len-20)
581                 .attr('cy',10).attr('r', 3)
582                 .style('fill', "#7e77f8")
583                 .style('stroke', '#484e46')
584                 .style('stroke-width', '0.4px');
585             
586             svg.append('circle')
587                 .attr("cx",len-20)
588                 .attr('cy',20)
589                 .attr('r', 3)
590                 .style('fill', '#03e2dd')
591                 .style('stroke', '#484e46')
592                 .style('stroke-width', '0.4px');
593             
594             svg.append('circle')
595                 .attr('cx',len-20)
596                 .attr('cy',30)
597                 .attr('r', 3)
598                 .style('fill', '#aad4b0')
599                 .style('stroke', '#484e46')
600                 .style('stroke-width', '0.4px')
601                 .style('stroke-dasharray','2 1');
602            
603             svg.append('circle')
604                 .attr('cx',len-50)
605                 .attr('cy',10).attr('r', 3)
606                 .style('fill', '#ffffff')
607                 .style('stroke', '#f39191')
608                 .style('stroke-width', '0.4px');
609             
610             svg.append('text')
611                 .attr('x', len-15)
612                 .attr('y', 10)
613                 .text('Leader')
614                 .style('font-size', '4px')
615                 .attr('alignment-baseline','middle');
616             
617             svg.append('text')
618                 .attr('x', len-15)
619                 .attr('y',20 )
620                 .text('Router')
621                 .style('font-size', '4px')
622                 .attr('alignment-baseline','middle');
623             
624             svg.append('text')
625                 .attr('x', len-15)
626                 .attr('y',30 )
627                 .text('Child')
628                 .style('font-size', '4px')
629                 .attr('alignment-baseline','middle');
630             
631             svg.append('text')
632                 .attr('x', len-45)
633                 .attr('y',10 )
634                 .text('Selected')
635                 .style('font-size', '4px')
636                 .attr('alignment-baseline','middle');
637
638             // Tooltip style  for each node
639             tooltip = d3.select('body')
640                 .append('div')
641                 .attr('class', 'tooltip')
642                 .style('position', 'absolute')
643                 .style('z-index', '10')
644                 .style('visibility', 'hidden')
645                 .text('a simple tooltip');
646
647             force = d3.layout.force()
648                 .distance(40)
649                 .size([len, len / (3 / 2)]);
650
651             
652             json = $scope.graphInfo;
653            
654             force
655                 .nodes(json.nodes)
656                 .links(json.links)
657                 .start();
658
659
660             var link = svg.selectAll('.link')
661                 .data(json.links)
662                 .enter().append('line')
663                 .attr('class', 'link')
664                 .style('stroke', '#908484')
665                 // Dash line for link between child and parent
666                 .style('stroke-dasharray', function(item) {
667                     if ('Timeout' in item.linkInfo) return '4 4';
668                     else return '0 0'
669                 })
670                 // Line width representing link quality
671                 .style('stroke-width', function(item) {
672                     if ('inQuality' in item.linkInfo)
673                         return Math.sqrt(item.linkInfo.inQuality/2);
674                     else return Math.sqrt(0.5)
675                 })
676                 // Effect of mouseover on a line
677                 .on('mouseover', function(item) {
678                     return tooltip.style('visibility', 'visible')
679                         .text(item.linkInfo);
680                 })
681                 .on('mousemove', function() {
682                     return tooltip.style('top', (d3.event.pageY - 10) + 'px')
683                         .style('left', (d3.event.pageX + 10) + 'px');
684                 })
685                 .on('mouseout', function() {
686                     return tooltip.style('visibility', 'hidden');
687                 });
688
689
690             var node = svg.selectAll('.node')
691                 .data(json.nodes)
692                 .enter().append('g')
693                 .attr('class', function(item) {
694                         return item.Role;
695                 })
696                 .call(force.drag)
697                 // Tooltip effect of mouseover on a node 
698                 .on('mouseover', function(item) {
699                     return tooltip.style('visibility', 'visible')
700                                   .text(item.Rloc16 );
701                 })
702                 .on('mousemove', function() {
703                     return tooltip.style('top', (d3.event.pageY - 10) + 'px')
704                                   .style('left', (d3.event.pageX + 10) + 'px');
705                 })
706                 .on('mouseout', function() {
707                     return tooltip.style('visibility', 'hidden');
708                 });
709
710             d3.selectAll('.Child')
711                 .append('circle')
712                 .attr('r', '6')
713                 .attr('fill', '#aad4b0')
714                 .style('stroke', '#484e46')
715                 .style('stroke-dasharray','2 1')
716                 .style('stroke-width', '0.5px')
717                 .attr('class', function(item) {
718                     return item.Rloc16;
719                 })
720                 .on('mouseover', function(item) {
721                     return tooltip.style('visibility', 'visible')
722                                   .text(item.Rloc16 );
723                 })
724                 .on('mousemove', function() {
725                     return tooltip.style('top', (d3.event.pageY - 10) + 'px')
726                                   .style('left', (d3.event.pageX + 10) + 'px');
727                 })
728                 .on('mouseout', function() {
729                     return tooltip.style('visibility', 'hidden');
730                 });
731
732
733             d3.selectAll('.Leader')
734                 .append('circle')
735                 .attr('r', '8')
736                 .attr('fill', '#7e77f8')
737                 .style('stroke', '#484e46')
738                 .style('stroke-width', '1px')
739                 .attr('class', function(item) {
740                     return 'Stroke';
741                 })
742                 // Effect that node will become bigger when mouseover
743                 .on('mouseover', function(item) {
744                     d3.select(this)
745                         .transition()
746                         .attr('r','9');
747                     return tooltip.style('visibility', 'visible')
748                                   .text(item.Rloc16);
749                 })
750                 .on('mousemove', function() {
751                     return tooltip.style('top', (d3.event.pageY - 10) + 'px')
752                                   .style('left', (d3.event.pageX + 10) + 'px');
753                 })
754                 .on('mouseout', function() {
755                     d3.select(this).transition().attr('r','8');
756                         return tooltip.style('visibility', 'hidden');
757                 })
758                 // Effect that node will have a yellow edge when clicked
759                 .on('click', function(item) {
760                     d3.selectAll('.Stroke')
761                         .style('stroke', '#484e46')
762                         .style('stroke-width', '1px');
763                     d3.select(this)
764                         .style('stroke', '#f39191')
765                         .style('stroke-width', '1px');
766                     $scope.$apply(function() {
767                         $scope.nodeDetailInfo = item;
768                         $scope.updateDetailLabel();
769                     });
770                 });
771             d3.selectAll('.Router')
772                 .append('circle')
773                 .attr('r', '8')
774                 .style('stroke', '#484e46')
775                 .style('stroke-width', '1px')
776                 .attr('fill', '#03e2dd')
777                 .attr('class','Stroke')
778                 .on('mouseover', function(item) {
779                     d3.select(this)
780                         .transition()
781                         .attr('r','8');
782                     return tooltip.style('visibility', 'visible')
783                                   .text(item.Rloc16);
784                 })
785                 .on('mousemove', function() {
786                     return tooltip.style('top', (d3.event.pageY - 10) + 'px')
787                                   .style('left', (d3.event.pageX + 10) + 'px');
788                 })
789                 .on('mouseout', function() {
790                     d3.select(this)
791                         .transition()
792                         .attr('r','7');
793                     return tooltip.style('visibility', 'hidden');
794                 })
795                 // The same effect as Leader
796                 .on('click', function(item) {
797                     d3.selectAll('.Stroke')
798                         .style('stroke', '#484e46')
799                         .style('stroke-width', '1px');
800                     d3.select(this)
801                         .style('stroke', '#f39191')
802                         .style('stroke-width', '1px');
803                     $scope.$apply(function() {
804                         $scope.nodeDetailInfo = item;
805                         $scope.updateDetailLabel();
806                     });
807                 });
808
809             force.on('tick', function() {
810                 link.attr('x1', function(item) { return item.source.x; })
811                     .attr('y1', function(item) { return item.source.y; })
812                     .attr('x2', function(item) { return item.target.x; })
813                     .attr('y2', function(item) { return item.target.y; });
814                 node.attr('transform', function(item) {
815                     return 'translate(' + item.x + ',' + item.y + ')';
816                 });
817             });
818             
819             $scope.updateDetailLabel();
820             $scope.graphisReady = true;
821
822         }
823     };
824 })();