GUI2 Display of mastership for devices

Change-Id: I13ed95d1a58d055aa913c69402541b87855c28c8
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
index 35c3fd4..fb0569b 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.html
@@ -67,7 +67,7 @@
         overlaid on the above. This is the blue box, and its width and height does
         not change
     -->
-    <svg:rect x="-16" y="-16" width="32" height="32" [ngStyle]="{'fill': panelColour(0)}">
+    <svg:rect x="-16" y="-16" width="32" height="32" [ngStyle]="{'fill': panelColor}">
     </svg:rect>
     <svg:path *ngIf="device.location && device.location.locType != 'none'"
               d="M-15 12 v3 h5" style="stroke: white; stroke-width: 1; fill: none"></svg:path>
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
index b2f0592..994967d 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.spec.ts
@@ -16,12 +16,13 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { DeviceNodeSvgComponent } from './devicenodesvg.component';
-import {FnService, IconService, LogService} from 'gui2-fw-lib';
+import {FnService, IconService, LogService, SvgUtilService} from 'gui2-fw-lib';
 import {ActivatedRoute, Params} from '@angular/router';
 import {of} from 'rxjs';
 import {ChangeDetectorRef} from '@angular/core';
 import {Device} from '../../models';
 import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {TopologyService} from '../../../../topology.service';
 
 class MockActivatedRoute extends ActivatedRoute {
     constructor(params: Params) {
@@ -34,6 +35,40 @@
     loadIconDef() { }
 }
 
+class MockSvgUtilService {
+
+    cat7() {
+        const tcid = 'd3utilTestCard';
+
+        function getColor(id, muted, theme) {
+            // NOTE: since we are lazily assigning domain ids, we need to
+            //       get the color from all 4 scales, to keep the domains
+            //       in sync.
+            const ln = '#5b99d2';
+            const lm = '#9ebedf';
+            const dn = '#5b99d2';
+            const dm = '#9ebedf';
+            if (theme === 'dark') {
+                return muted ? dm : dn;
+            } else {
+                return muted ? lm : ln;
+            }
+        }
+
+        return {
+            // testCard: testCard,
+            getColor: getColor,
+        };
+    }
+}
+
+class MockTopologyService {
+    public instancesIndex: Map<string, number>;
+    constructor() {
+        this.instancesIndex = new Map();
+    }
+}
+
 describe('DeviceNodeSvgComponent', () => {
     let fs: FnService;
     let logServiceSpy: jasmine.SpyObj<LogService>;
@@ -71,6 +106,8 @@
                 { provide: ActivatedRoute, useValue: ar },
                 { provide: ChangeDetectorRef, useClass: ChangeDetectorRef },
                 { provide: IconService, useClass: MockIconService },
+                { provide: SvgUtilService, useClass: MockSvgUtilService },
+                { provide: TopologyService, useClass: MockTopologyService },
                 { provide: 'Window', useValue: windowMock },
             ]
         })
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
index d40f413..9c246d9 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/layer/forcesvg/visuals/devicenodesvg/devicenodesvg.component.ts
@@ -19,7 +19,7 @@
     Component,
     EventEmitter,
     Input,
-    OnChanges, Output,
+    OnChanges, OnInit, Output,
     SimpleChanges,
 } from '@angular/core';
 import {Device, LabelToggle, UiElement} from '../../models';
@@ -27,6 +27,7 @@
 import {NodeVisual, SelectedEvent} from '../nodevisual';
 import {animate, state, style, transition, trigger} from '@angular/animations';
 import {LocationType} from '../../../backgroundsvg/backgroundsvg.component';
+import {TopologyService} from '../../../../topology.service';
 
 /**
  * The Device node in the force graph
@@ -64,21 +65,30 @@
         ])
     ]
 })
-export class DeviceNodeSvgComponent extends NodeVisual implements OnChanges {
+export class DeviceNodeSvgComponent extends NodeVisual implements OnInit, OnChanges {
     @Input() device: Device;
     @Input() scale: number = 1.0;
     @Input() labelToggle: LabelToggle.Enum = LabelToggle.Enum.NONE;
+    @Input() colorMuted: boolean = false;
+    @Input() colorTheme: string = 'light';
     @Output() selectedEvent = new EventEmitter<SelectedEvent>();
     textWidth: number = 36;
+    panelColor: string = '#9ebedf';
+
     constructor(
         protected log: LogService,
         private is: IconService,
         protected sus: SvgUtilService,
+        protected ts: TopologyService,
         private ref: ChangeDetectorRef
     ) {
         super();
     }
 
+    ngOnInit(): void {
+        this.panelColor = this.panelColour();
+    }
+
     /**
      * Called by parent (forcesvg) when a change happens
      *
@@ -92,6 +102,13 @@
                 this.device.x = 0;
                 this.device.y = 0;
             }
+            // The master might have changed - recalculate color
+            this.panelColor = this.panelColour();
+        }
+
+        if (changes['colorMuted']) {
+            this.colorMuted = changes['colorMuted'].currentValue;
+            this.panelColor = this.panelColour();
         }
         this.ref.markForCheck();
     }
@@ -137,7 +154,8 @@
      * Get a colour for the banner of the nth panel
      * @param idx The index of the panel (0-6)
      */
-    panelColour(idx: number): string {
-        return this.sus.cat7().getColor(idx, false, '');
+    panelColour(): string {
+        const idx = this.ts.instancesIndex.get(this.device.master);
+        return this.sus.cat7().getColor(idx, this.colorMuted, this.colorTheme);
     }
 }