GUI2 Extract Topology view in to its own library
Change-Id: I45597d0902c99b5b3d606966866cc518011c54a0
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.css
new file mode 100644
index 0000000..05402c8
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.css
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Details Panel --- */
+
+#topo2-p-detail {
+ padding: 16px;
+ opacity: 1;
+ right: 20px;
+ width: 260px;
+ top: 390px;
+}
+
+#topo2-p-detail div.actionBtns {
+ padding-top: 6px;
+}
+
+html[data-platform='iPad'] {
+ top: 386px;
+}
+
+.actionBtns .actionBtn {
+ display: inline-block;
+}
+.actionBtns .actionBtn svg {
+ width: 28px;
+ height: 28px;
+}
+
+div.actionBtns {
+ padding-top: 6px;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.html b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.html
new file mode 100644
index 0000000..ca23ca1
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.html
@@ -0,0 +1,66 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo2-p-detail" class="floatpanel topo2-p"
+ [@detailsPanelState]="on && selectedNode !== undefined">
+ <!-- Template explanation - Create a HTML header which has an SVG icon along
+ side title text. -->
+ <div class="header">
+ <div class="icon clickable">
+ <onos-icon
+ [iconSize]="26"
+ [iconId]="showDetails?.glyphId">
+ </onos-icon>
+ </div>
+ <h2 class="clickable">{{ showDetails?.title }}</h2>
+ </div>
+ <div class="body">
+ <table>
+ <tbody>
+ <!-- Template explanation - Inside a HTML table, create a row per
+ item in the propOrder array returned through the WSS showDetails.
+ If the row name contains only '-' then draw a horiz rule otherwise
+ create a cell for the name and another for the value -->
+ <tr *ngFor="let p of showDetails?.propOrder">
+ <td *ngIf="showDetails?.propLabels[p] !== '-'"
+ class="label">{{showDetails?.propLabels[p]}} :</td>
+ <td *ngIf="showDetails?.propLabels[p] !== '-'"
+ class="value">{{ showDetails?.propValues[p]}}</td>
+ <!-- If the label is '-' then insert a horiz line -->
+ <td *ngIf="showDetails?.propLabels[p] === '-'"
+ colspan="2"><hr></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div class="footer">
+ <hr>
+ <div class="actionBtns">
+ <!-- Template explanation - Inside the panel footer, create an SVG icon
+ per entry in the buttons array returned from the WSS showDetails
+ The icons used here are loaded in the ForceSvgComponent
+ -->
+ <div *ngFor="let btn of showDetails?.buttons" class="actionBtn">
+ <onos-icon id="topo2-p-detail-core-{{ btn }}"
+ (click)="navto(buttonAttribs(btn).path, showDetails.navPath, showDetails.id)"
+ [iconSize]="25"
+ [iconId]="buttonAttribs(btn).gid"
+ [toolTip]="lionFn(buttonAttribs(btn).tt)"
+ classes="button icon selected">
+ </onos-icon>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.spec.ts
new file mode 100644
index 0000000..620d931
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.spec.ts
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { of } from 'rxjs';
+import { DetailsComponent } from './details.component';
+
+import {
+ FnService, LionService,
+ LogService, IconComponent
+} from 'gui2-fw-lib';
+import {RouterTestingModule} from '@angular/router/testing';
+
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
+ }
+}
+
+/**
+ * ONOS GUI -- Topology View Details Panel-- Unit Tests
+ */
+describe('DetailsComponent', () => {
+ let fs: FnService;
+ let ar: MockActivatedRoute;
+ let windowMock: Window;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
+ let component: DetailsComponent;
+ let fixture: ComponentFixture<DetailsComponent>;
+
+ const bundleObj = {
+ 'core.view.Flow': {
+ test: 'test1'
+ }
+ };
+ const mockLion = (key) => {
+ return bundleObj[key] || '%' + key + '%';
+ };
+
+ beforeEach(async(() => {
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+ ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+ windowMock = <any>{
+ location: <any>{
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true' },
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, windowMock);
+
+ TestBed.configureTestingModule({
+ imports: [ BrowserAnimationsModule, RouterTestingModule ],
+ declarations: [ DetailsComponent, IconComponent ],
+ providers: [
+ { provide: FnService, useValue: fs },
+ { provide: LogService, useValue: logSpy },
+ {
+ provide: LionService, useFactory: (() => {
+ return {
+ bundle: ((bundleId) => mockLion),
+ ubercache: new Array(),
+ loadCbs: new Map<string, () => void>([])
+ };
+ })
+ },
+ { provide: 'Window', useValue: windowMock },
+ ]
+ })
+ .compileComponents();
+ logServiceSpy = TestBed.get(LogService);
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DetailsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.ts
new file mode 100644
index 0000000..ea1462c
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.ts
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+ Component,
+ Input,
+ OnChanges,
+ OnDestroy,
+ OnInit,
+ SimpleChanges
+} from '@angular/core';
+import {animate, state, style, transition, trigger} from '@angular/animations';
+import {
+ DetailsPanelBaseImpl,
+ FnService, LionService,
+ LoadingService,
+ LogService,
+ WebSocketService
+} from 'gui2-fw-lib';
+import {Host, Link, LinkType, UiElement} from '../../layer/forcesvg/models';
+import {Params, Router} from '@angular/router';
+
+
+interface ButtonAttrs {
+ gid: string;
+ tt: string;
+ path: string;
+}
+
+const SHOWDEVICEVIEW: ButtonAttrs = {
+ gid: 'deviceTable',
+ tt: 'tt_ctl_show_device',
+ path: 'device',
+};
+const SHOWFLOWVIEW: ButtonAttrs = {
+ gid: 'flowTable',
+ tt: 'title_flows',
+ path: 'flow',
+};
+const SHOWPORTVIEW: ButtonAttrs = {
+ gid: 'portTable',
+ tt: 'tt_ctl_show_port',
+ path: 'port',
+};
+const SHOWGROUPVIEW: ButtonAttrs = {
+ gid: 'groupTable',
+ tt: 'tt_ctl_show_group',
+ path: 'group',
+};
+const SHOWMETERVIEW: ButtonAttrs = {
+ gid: 'meterTable',
+ tt: 'tt_ctl_show_meter',
+ path: 'meter',
+};
+const SHOWPIPECONFVIEW: ButtonAttrs = {
+ gid: 'pipeconfTable',
+ tt: 'tt_ctl_show_pipeconf',
+ path: 'pipeconf',
+};
+
+interface ShowDetails {
+ buttons: string[];
+ glyphId: string;
+ id: string;
+ navPath: string;
+ propLabels: Object;
+ propOrder: string[];
+ propValues: Object;
+ title: string;
+}
+/**
+ * ONOS GUI -- Topology Details Panel.
+ * Displays details of selected device. When no device is selected the panel slides
+ * off to the side and disappears
+ */
+@Component({
+ selector: 'onos-details',
+ templateUrl: './details.component.html',
+ styleUrls: [
+ './details.component.css', './details.theme.css',
+ '../../topology.common.css',
+ '../../../../fw/widget/panel.css', '../../../../fw/widget/panel-theme.css'
+ ],
+ animations: [
+ trigger('detailsPanelState', [
+ state('true', style({
+ transform: 'translateX(0%)',
+ opacity: '1.0'
+ })),
+ state('false', style({
+ transform: 'translateX(100%)',
+ opacity: '0'
+ })),
+ transition('0 => 1', animate('100ms ease-in')),
+ transition('1 => 0', animate('100ms ease-out'))
+ ])
+ ]
+})
+export class DetailsComponent extends DetailsPanelBaseImpl implements OnInit, OnDestroy, OnChanges {
+ @Input() selectedNode: UiElement = undefined; // Populated when user selects node or link
+ @Input() on: boolean = false; // Override the parent class attribute
+
+ // deferred localization strings
+ lionFn; // Function
+ showDetails: ShowDetails; // Will be populated on callback. Cleared if nothing is selected
+
+ constructor(
+ protected fs: FnService,
+ protected log: LogService,
+ protected ls: LoadingService,
+ protected router: Router,
+ protected wss: WebSocketService,
+ private lion: LionService
+ ) {
+ super(fs, ls, log, wss, 'topo');
+
+ if (this.lion.ubercache.length === 0) {
+ this.lionFn = this.dummyLion;
+ this.lion.loadCbs.set('flow', () => this.doLion());
+ } else {
+ this.doLion();
+ }
+
+ this.log.debug('Topo DetailsComponent constructed');
+ }
+
+ /**
+ * When the component is initializing set up the handler for callbacks of
+ * ShowDetails from the WSS. Set the variable showDetails when ever a callback
+ * is made
+ */
+ ngOnInit(): void {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ ['showDetails', (data) => {
+ this.showDetails = data;
+ // this.log.debug('showDetails received', data);
+ }
+ ]
+ ]));
+ this.log.debug('Topo DetailsComponent initialized');
+ }
+
+ /**
+ * When the component is being unloaded then unbind the WSS handler.
+ */
+ ngOnDestroy(): void {
+ this.wss.unbindHandlers(['showDetails']);
+ this.log.debug('Topo DetailsComponent destroyed');
+ }
+
+ /**
+ * If changes are detected on the Input param selectedNode, call on WSS sendEvent
+ * Note the difference in call to the WSS with requestDetails between a node
+ * and a link - the handling is done in TopologyViewMessageHandler#RequestDetails.process()
+ *
+ * The WSS will call back asynchronously (see fn in ngOnInit())
+ *
+ * @param changes Simple Changes set of updates
+ */
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes['selectedNode']) {
+ this.selectedNode = changes['selectedNode'].currentValue;
+ let type: any;
+
+ if (this.selectedNode === undefined) {
+ // Selection has been cleared
+ this.showDetails = <ShowDetails>{};
+ return;
+ }
+
+ if (this.selectedNode.hasOwnProperty('nodeType')) { // For Device, Host, SubRegion
+ type = (<Host>this.selectedNode).nodeType;
+ this.wss.sendEvent('requestDetails', {
+ id: this.selectedNode.id,
+ class: type,
+ });
+ } else if (this.selectedNode.hasOwnProperty('type')) { // Must be link
+ const link: Link = <Link>this.selectedNode;
+ if (<LinkType><unknown>LinkType[link.type] === LinkType.UiEdgeLink) { // Number based enum
+ this.wss.sendEvent('requestDetails', {
+ key: link.id,
+ class: 'link',
+ sourceId: link.epA,
+ targetId: Link.deviceNameFromEp(link.epB),
+ targetPort: link.portB,
+ isEdgeLink: true
+ });
+ } else {
+ this.wss.sendEvent('requestDetails', {
+ key: link.id,
+ class: 'link',
+ sourceId: Link.deviceNameFromEp(link.epA),
+ sourcePort: link.portA,
+ targetId: Link.deviceNameFromEp(link.epB),
+ targetPort: link.portB,
+ isEdgeLink: false
+ });
+ }
+ } else {
+ this.log.warn('Unexpected type for selected element', this.selectedNode);
+ }
+ }
+ }
+
+ /**
+ * Table of core button attributes to return per button icon
+ * @param btnName The name of the button
+ * @returns A structure with the button attributes
+ */
+ buttonAttribs(btnName: string): ButtonAttrs {
+ switch (btnName) {
+ case 'showDeviceView':
+ return SHOWDEVICEVIEW;
+ case 'showFlowView':
+ return SHOWFLOWVIEW;
+ case 'showPortView':
+ return SHOWPORTVIEW;
+ case 'showGroupView':
+ return SHOWGROUPVIEW;
+ case 'showMeterView':
+ return SHOWMETERVIEW;
+ case 'showPipeConfView':
+ return SHOWPIPECONFVIEW;
+ default:
+ return <ButtonAttrs>{
+ gid: btnName,
+ path: btnName
+ };
+ }
+ }
+
+ /**
+ * Navigate using Angular Routing. Combines the parameters to generate a relative URL
+ * e.g. if params are 'meter', 'device' and 'null:0000000000001' then the
+ * navigation URL will become "http://localhost:4200/#/meter?devId=null:0000000000000002"
+ *
+ * @param path The path to navigate to
+ * @param navPath The parameter name to use
+ * @param selId the parameter value to use
+ */
+ navto(path: string, navPath: string, selId: string): void {
+ this.log.debug('navigate to', path, 'for', navPath, '=', selId);
+ // Special case until it's fixed
+ if (selId) {
+ if (navPath === 'device') {
+ navPath = 'devId';
+ }
+ const queryPar: Params = {};
+ queryPar[navPath] = selId;
+ this.router.navigate([path], { queryParams: queryPar });
+ }
+ }
+
+ /**
+ * Read the LION bundle for Details panel and set up the lionFn
+ */
+ doLion() {
+ this.lionFn = this.lion.bundle('core.view.Flow');
+
+ }
+
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.theme.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.theme.css
new file mode 100644
index 0000000..7ad72dd
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.theme.css
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Details Panel Theme --- */
+
+#topo2-p-detail svg {
+ background: none;
+}
+
+#topo2-p-detail .header svg .glyph {
+ fill: #c0242b;
+}
+
+.dark #topo2-p-detail .header svg .glyph {
+ fill: #91292f;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.css
new file mode 100644
index 0000000..f335726
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Instance Panel --- */
+
+#topo-p-instance div.onosInst {
+ display: inline-block;
+ width: 170px;
+ height: 85px;
+ cursor: pointer;
+}
+
+#topo-p-instance svg text.instTitle {
+ font-size: 11pt;
+ font-weight: bold;
+ font-variant: small-caps;
+ text-transform: uppercase;
+}
+#topo-p-instance svg text.instLabel {
+ font-size: 10pt;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.html b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.html
new file mode 100644
index 0000000..f5a2859
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.html
@@ -0,0 +1,40 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo-p-instance" class="floatpanel" [ngStyle]="{'left': '20px', 'top':divTopPx+'px', 'width': (onosInstances.length * 170)+'px', 'height': '85px'}" [@instancePanelState]="on">
+ <div *ngFor="let inst of onosInstances | keyvalue ; let i=index"
+ [ngClass]="['onosInst', inst.value.online?'online':'', inst.value.ready? 'ready': '', mastership?'mastership':'', 'affinity']"
+ (click)="chooseMastership(inst.value.id)">
+ <svg xmlns="http://www.w3.org/2000/svg" width="170" height="85" viewBox="0 0 170 85">
+ <!-- The following blue-glow effect is applied (through CSS) when mastership style is activated on a rectangle -->
+ <filter x="-50%" y="-50%" width="200%" height="200%" id="blue-glow">
+ <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0 0 0 1 0 "></feColorMatrix>
+ <feGaussianBlur stdDeviation="3" result="coloredBlur"></feGaussianBlur>
+ <feMerge>
+ <feMergeNode in="coloredBlur"></feMergeNode>
+ <feMergeNode in="SourceGraphic"></feMergeNode>
+ </feMerge>
+ </filter>
+ <rect x="5" y="5" width="160" height="30" [ngStyle]="{ 'fill': panelColour(i)}"></rect>
+ <text class="instTitle" x="48" y="27">{{ inst.value.id }}</text>
+ <rect x="5" y="35" width="160" height="45"></rect>
+ <text class="instLabel ip" x="48" y="55">{{ inst.value.ip }}</text>
+ <use width="20" height="20" class="glyph badgeIcon bird" xlink:href="#bird" transform="translate(15,10)"></use>
+ <use *ngIf="inst.value.ready" width="16" height="16" class="glyph overlay badgeIcon readyBadge" xlink:href="#checkMark" transform="translate(18,40)"></use>
+ <text class="instLabel ns" x="48" y="73">{{lionFn('devices')}} {{ inst.value.switches }}</text>
+ <use *ngIf="inst.value.uiAttached" width="24" height="24" class="glyph overlay badgeIcon uiBadge" xlink:href="#uiAttached" transform="translate(14,54)"></use>
+ </svg>
+ </div>
+</div>
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.spec.ts
new file mode 100644
index 0000000..a03f23e
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.spec.ts
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { of } from 'rxjs';
+import { InstanceComponent } from './instance.component';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+import {
+ FnService,
+ LogService
+} from 'gui2-fw-lib';
+
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
+ }
+}
+
+/**
+ * ONOS GUI -- Topology View Instance Panel-- Unit Tests
+ */
+describe('InstanceComponent', () => {
+ let fs: FnService;
+ let ar: MockActivatedRoute;
+ let windowMock: Window;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
+ let component: InstanceComponent;
+ let fixture: ComponentFixture<InstanceComponent>;
+
+ beforeEach(async(() => {
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+ ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+ windowMock = <any>{
+ location: <any>{
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true' },
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, windowMock);
+
+ TestBed.configureTestingModule({
+ imports: [ BrowserAnimationsModule ],
+ declarations: [ InstanceComponent ],
+ providers: [
+ { provide: FnService, useValue: fs },
+ { provide: LogService, useValue: logSpy },
+ { provide: 'Window', useValue: windowMock },
+ ]
+ })
+ .compileComponents();
+ logServiceSpy = TestBed.get(LogService);
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InstanceComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.ts
new file mode 100644
index 0000000..487550d
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.component.ts
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+ Component,
+ Input,
+ Output,
+ EventEmitter
+} from '@angular/core';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import {
+ LogService,
+ LoadingService,
+ FnService,
+ PanelBaseImpl,
+ IconService,
+ SvgUtilService, LionService
+} from 'gui2-fw-lib';
+
+/**
+ * A model of instance information that drives each panel
+ */
+export interface Instance {
+ id: string;
+ ip: string;
+ online: boolean;
+ ready: boolean;
+ switches: number;
+ uiAttached: boolean;
+}
+
+/**
+ * ONOS GUI -- Topology Instances Panel.
+ * Displays ONOS instances. The onosInstances Array gets updated by topology.service
+ * whenever a topo2AllInstances update arrives back on the WebSocket
+ *
+ * This emits a mastership event when the user clicks on an instance, to
+ * see the devices that it has mastership of.
+ */
+@Component({
+ selector: 'onos-instance',
+ templateUrl: './instance.component.html',
+ styleUrls: [
+ './instance.component.css', './instance.theme.css',
+ '../../topology.common.css',
+ '../../../../fw/widget/panel.css', '../../../../fw/widget/panel-theme.css'
+ ],
+ animations: [
+ trigger('instancePanelState', [
+ state('true', style({
+ transform: 'translateX(0%)',
+ opacity: '1.0'
+ })),
+ state('false', style({
+ transform: 'translateX(-100%)',
+ opacity: '0.0'
+ })),
+ transition('0 => 1', animate('100ms ease-in')),
+ transition('1 => 0', animate('100ms ease-out'))
+ ])
+ ]
+})
+export class InstanceComponent extends PanelBaseImpl {
+ @Input() divTopPx: number = 100;
+ @Input() on: boolean = false; // Override the parent class attribute
+ @Output() mastershipEvent = new EventEmitter<string>();
+ public onosInstances: Array<Instance>;
+ public mastership: string;
+ lionFn; // Function
+
+ constructor(
+ protected fs: FnService,
+ protected log: LogService,
+ protected ls: LoadingService,
+ protected is: IconService,
+ protected sus: SvgUtilService,
+ private lion: LionService
+ ) {
+ super(fs, ls, log);
+ this.onosInstances = <Array<Instance>>[];
+
+ if (this.lion.ubercache.length === 0) {
+ this.lionFn = this.dummyLion;
+ this.lion.loadCbs.set('topo-inst', () => this.doLion());
+ } else {
+ this.doLion();
+ }
+ this.log.debug('InstanceComponent constructed');
+ }
+
+ /**
+ * 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, '');
+ }
+
+ /**
+ * Toggle the display of mastership
+ * If the same instance is clicked a second time then cancel display of mastership
+ * @param instId The instance to display mastership for
+ */
+ chooseMastership(instId: string): void {
+ if (this.mastership === instId) {
+ this.mastership = '';
+ } else {
+ this.mastership = instId;
+ }
+ this.mastershipEvent.emit(this.mastership);
+ this.log.debug('Instance', this.mastership, 'chosen on GUI');
+ }
+
+ /**
+ * Read the LION bundle for Details panel and set up the lionFn
+ */
+ doLion() {
+ this.lionFn = this.lion.bundle('core.view.Topo');
+
+ }
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.theme.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.theme.css
new file mode 100644
index 0000000..3be7bdd
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/instance/instance.theme.css
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* --- Topo Instance Panel --- */
+
+#topo-p-instance svg rect {
+ stroke-width: 0;
+ fill: #fbfbfb;
+}
+
+/* body of an instance */
+#topo-p-instance .online svg rect {
+ opacity: 1;
+ fill: #fbfbfb;
+}
+
+#topo-p-instance svg .glyph {
+ fill: #fff;
+}
+#topo-p-instance .online svg .glyph {
+ fill: #fff;
+}
+.dark #topo-p-instance .online svg .glyph.overlay {
+ fill: #fff;
+}
+
+/* offline */
+#topo-p-instance svg .badgeIcon {
+ opacity: 0.4;
+ fill: #939598;
+}
+
+/* online */
+#topo-p-instance .online svg .badgeIcon {
+ opacity: 1.0;
+ fill: #939598;
+}
+#topo-p-instance .online svg .badgeIcon.bird {
+ fill: #ffffff;
+}
+
+#topo-p-instance svg .readyBadge {
+ visibility: hidden;
+}
+#topo-p-instance .ready svg .readyBadge {
+ visibility: visible;
+}
+
+#topo-p-instance svg text {
+ text-anchor: start;
+ opacity: 0.5;
+ fill: #3c3a3a;
+}
+
+#topo-p-instance .online svg text {
+ opacity: 1.0;
+ fill: #3c3a3a;
+}
+
+#topo-p-instance .onosInst.mastership {
+ opacity: 0.3;
+}
+#topo-p-instance .onosInst.mastership.affinity {
+ opacity: 1.0;
+}
+#topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
+}
+
+.firefox #topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
+}
+
+.dark #topo-p-instance {
+ background-color: #2f313c;
+ color: #c2c2b7;
+ border: 1px solid #364144;
+
+}
+
+.dark #topo-p-instance svg rect {
+ stroke-width: 0;
+ fill: #525660;
+}
+
+/* body of an instance */
+.dark #topo-p-instance .online svg rect {
+ opacity: 1;
+ fill: #838992;
+}
+
+.dark #topo-p-instance svg .glyph {
+ fill: #ddd;
+}
+.dark #topo-p-instance .online svg .glyph {
+ fill: #fff;
+}
+.dark #topo-p-instance .online svg .glyph.overlay {
+ fill: #c7c7c7;
+}
+
+/* offline */
+.dark #topo-p-instance svg .badgeIcon {
+ opacity: 0.4;
+ fill: #939598;
+}
+
+/* online */
+.dark #topo-p-instance .online svg .badgeIcon {
+ opacity: 1.0;
+ fill: #939598;
+}
+.dark #topo-p-instance .online svg .badgeIcon.bird {
+ fill: #ffffff;
+}
+
+.dark #topo-p-instance svg text {
+ text-anchor: start;
+ opacity: 0.5;
+ fill: #aaa;
+}
+
+.dark #topo-p-instance .online svg text {
+ opacity: 1.0;
+ fill: #fff;
+}
+
+.dark #topo-p-instance .onosInst.mastership {
+ opacity: 0.3;
+}
+.dark #topo-p-instance .onosInst.mastership.affinity {
+ opacity: 1.0;
+}
+.dark #topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
+}
+
+.dark.firefox #topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.css
new file mode 100644
index 0000000..59c0d78
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.css
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ONOS GUI -- Topology Map Selector -- CSS file
+ */
+.dialog h2 {
+ margin: 0;
+ word-wrap: break-word;
+ display: inline-block;
+ width: 210px;
+ vertical-align: middle;
+}
+
+.dialog .dialog-button {
+ display: inline-block;
+ cursor: pointer;
+ height: 20px;
+ padding: 6px 8px 2px 8px;
+ margin: 4px;
+ float: right;
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.html b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.html
new file mode 100644
index 0000000..579a43b
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.html
@@ -0,0 +1,33 @@
+<!--
+~ Copyright 2019-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo-p-dialog"
+ class="floatpanel dialog topo-p"
+ style="opacity: 1; left: 20px; width: 300px;">
+ <div class="header">
+ <h2>{{ lionFn('title_select_map') }}</h2>
+ </div>
+ <div class="map-list">
+ <form [formGroup]="form">
+ <select formControlName="mapid">
+ <option *ngFor="let o of mapSelectorResponse.order" [ngValue]="o">{{ mapSelectorResponse.maps[o]['description'] }}</option>
+ </select>
+ </form>
+ </div>
+ <div class="footer">
+ <div class="dialog-button" (click)="choice(form.value)">{{ lionFn('ok') }}</div>
+ <div class="dialog-button" (click)="choice(undefined)">{{ lionFn('close') }}</div>
+ </div>
+</div>
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.spec.ts
new file mode 100644
index 0000000..4d95361
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.spec.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MapSelectorComponent } from './mapselector.component';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {FnService, LogService} from 'gui2-fw-lib';
+
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
+ }
+}
+
+describe('MapSelectorComponent', () => {
+ let fs: FnService;
+ let ar: MockActivatedRoute;
+ let windowMock: Window;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
+ let component: MapSelectorComponent;
+ let fixture: ComponentFixture<MapSelectorComponent>;
+
+ beforeEach(async(() => {
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+ ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+ windowMock = <any>{
+ location: <any>{
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true' },
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, windowMock);
+
+ TestBed.configureTestingModule({
+ imports: [
+ FormsModule,
+ ReactiveFormsModule
+ ],
+ declarations: [ MapSelectorComponent ],
+ providers: [
+ { provide: FnService, useValue: fs },
+ { provide: LogService, useValue: logSpy },
+ { provide: 'Window', useValue: windowMock },
+ ]
+ })
+ .compileComponents();
+ logServiceSpy = TestBed.get(LogService);
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MapSelectorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
+
+// Expecting WebSocket request and response similar to:
+//
+// {"event":"mapSelectorRequest","payload":{}}
+//
+// {
+// "event": "mapSelectorResponse",
+// "payload": {
+// "order": ["australia", "americas", "n_america", "s_america", "usa", "bayareaGEO",
+// "europe", "italy", "uk", "japan", "s_korea", "taiwan", "africa", "oceania", "asia"],
+// "maps": {
+// "australia": {
+// "id": "australia",
+// "description": "Australia",
+// "filePath": "*australia",
+// "scale": 1.0
+// },
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.ts
new file mode 100644
index 0000000..c97e0ca
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.component.ts
@@ -0,0 +1,87 @@
+
+import {
+ Component, EventEmitter, OnChanges,
+ OnDestroy,
+ OnInit, Output, SimpleChanges,
+} from '@angular/core';
+import {
+ DetailsPanelBaseImpl,
+ FnService,
+ LionService, LoadingService,
+ LogService,
+ WebSocketService
+} from 'gui2-fw-lib';
+import {FormControl, FormGroup} from '@angular/forms';
+import { MapObject } from '../../layer/maputils';
+
+interface MapSelection {
+ order: string[];
+ maps: Object[];
+}
+
+@Component({
+ selector: 'onos-mapselector',
+ templateUrl: './mapselector.component.html',
+ styleUrls: ['./mapselector.component.css', './mapselector.theme.css', '../../topology.common.css']
+})
+export class MapSelectorComponent extends DetailsPanelBaseImpl implements OnInit, OnDestroy {
+ @Output() chosenMap = new EventEmitter<MapObject>();
+ lionFn; // Function
+ mapSelectorResponse: MapSelection = <MapSelection>{
+ order: [],
+ maps: []
+ };
+ form = new FormGroup({
+ mapid: new FormControl(this.mapSelectorResponse.order[0]),
+ });
+
+ constructor(
+ protected fs: FnService,
+ protected log: LogService,
+ protected ls: LoadingService,
+ protected wss: WebSocketService,
+ private lion: LionService
+ ) {
+ super(fs, ls, log, wss, 'topo');
+
+ if (this.lion.ubercache.length === 0) {
+ this.lionFn = this.dummyLion;
+ this.lion.loadCbs.set('topoms', () => this.doLion());
+ } else {
+ this.doLion();
+ }
+
+ this.log.debug('Topo MapSelectorComponent constructed');
+ }
+
+ ngOnInit() {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ ['mapSelectorResponse', (data) => {
+ this.mapSelectorResponse = data;
+ this.form.setValue({'mapid': this.mapSelectorResponse.order[0]});
+ }
+ ]
+ ]));
+ this.wss.sendEvent('mapSelectorRequest', {});
+ this.log.debug('Topo MapSelectorComponent initialized');
+ }
+
+ /**
+ * When the component is being unloaded then unbind the WSS handler.
+ */
+ ngOnDestroy(): void {
+ this.wss.unbindHandlers(['mapSelectorResponse']);
+ this.log.debug('Topo MapSelectorComponent destroyed');
+ }
+
+ /**
+ * Read the LION bundle for Details panel and set up the lionFn
+ */
+ doLion() {
+ this.lionFn = this.lion.bundle('core.view.Topo');
+ }
+
+ choice(mapid: Object): void {
+ this.chosenMap.emit(<MapObject>this.mapSelectorResponse.maps[mapid['mapid']]);
+ }
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.theme.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.theme.css
new file mode 100644
index 0000000..0ef1538
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/mapselector/mapselector.theme.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ONOS GUI -- Topology Map Selector theme -- CSS file
+ */
+
+/*.light */
+.dialog .dialog-button {
+ background-color: #518ecc;
+ color: white;
+}
+
+
+/* ========== DARK Theme ========== */
+
+.dark .dialog .dialog-button {
+ background-color: #345e85;
+ color: #cccccd;
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.css
new file mode 100644
index 0000000..b4bd37b
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.css
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology Summary Panel -- CSS file
+ */
+#topo2-p-summary {
+ padding: 16px;
+ top: 100px;
+}
+
+#topo2-p-summary td.label {
+ width: 50%;
+}
+
+#topo2-p div.header div.icon {
+ padding: 10px
+}
+
+#topo2-p-summary div.header h2 {
+ padding: 10px;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.html b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.html
new file mode 100644
index 0000000..d781418
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.html
@@ -0,0 +1,20 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="topo2-p-summary" class="floatpanel topo2-p"
+ style="opacity: 1; right: 20px; width: 260px;" [@summaryPanelState]="on">
+ <!-- everything else is filled in dynamically by listProps() and the
+ response showSummary received from the server -->
+</div>
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.spec.ts
new file mode 100644
index 0000000..5f69c19
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.spec.ts
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { of } from 'rxjs';
+import { SummaryComponent } from './summary.component';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+import {
+ FnService,
+ LogService
+} from 'gui2-fw-lib';
+
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
+ }
+}
+
+/**
+ * ONOS GUI -- Topology View Summary Panel -- Unit Tests
+ */
+describe('SummaryComponent', () => {
+ let fs: FnService;
+ let ar: MockActivatedRoute;
+ let windowMock: Window;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
+ let component: SummaryComponent;
+ let fixture: ComponentFixture<SummaryComponent>;
+
+ beforeEach(async(() => {
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+ ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+ windowMock = <any>{
+ location: <any>{
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true' },
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, windowMock);
+
+ TestBed.configureTestingModule({
+ imports: [ BrowserAnimationsModule ],
+ declarations: [ SummaryComponent ],
+ providers: [
+ { provide: FnService, useValue: fs },
+ { provide: LogService, useValue: logSpy },
+ { provide: 'Window', useValue: windowMock },
+ ]
+ })
+ .compileComponents();
+ logServiceSpy = TestBed.get(LogService);
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SummaryComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.ts
new file mode 100644
index 0000000..3a42a0b
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/summary/summary.component.ts
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+ Component,
+ Input,
+ OnDestroy,
+ OnInit,
+ ViewEncapsulation
+} from '@angular/core';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import * as d3 from 'd3';
+import { TopoPanelBaseImpl } from '../topopanel.base';
+import {
+ LogService,
+ LoadingService,
+ FnService,
+ WebSocketService,
+ GlyphService
+} from 'gui2-fw-lib';
+
+export interface SummaryResponse {
+ title: string;
+}
+/**
+ * ONOS GUI -- Topology Summary Module.
+ * Defines modeling of ONOS Summary Panel.
+ * Note: This component uses the d3 DOM building technique from the old GUI - this
+ * is not the Angular way of building components and should be avoided generally
+ * See DetailsPanelComponent for a better way of doing this kind of thing
+ */
+@Component({
+ selector: 'onos-summary',
+ templateUrl: './summary.component.html',
+ styleUrls: [
+ './summary.component.css',
+ '../../topology.common.css', '../../topology.theme.css',
+ '../../../../fw/widget/panel.css', '../../../../fw/widget/panel-theme.css'
+ ],
+ encapsulation: ViewEncapsulation.None,
+ animations: [
+ trigger('summaryPanelState', [
+ state('true', style({
+ transform: 'translateX(0%)',
+ opacity: '100'
+ })),
+ state('false', style({
+ transform: 'translateX(100%)',
+ opacity: '0'
+ })),
+ transition('0 => 1', animate('100ms ease-in')),
+ transition('1 => 0', animate('100ms ease-out'))
+ ])
+ ]
+})
+export class SummaryComponent extends TopoPanelBaseImpl implements OnInit, OnDestroy {
+ @Input() on: boolean = false; // Override the parent class attribute
+ private handlers: string[] = [];
+ private resp: string = 'showSummary';
+ private summaryData: SummaryResponse;
+
+ constructor(
+ protected fs: FnService,
+ protected log: LogService,
+ protected ls: LoadingService,
+ protected wss: WebSocketService,
+ protected gs: GlyphService
+ ) {
+ super(fs, ls, log, 'summary');
+ this.summaryData = <SummaryResponse>{};
+ this.log.debug('SummaryComponent constructed');
+ }
+
+
+ ngOnInit() {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ [this.resp, (data) => this.handleSummaryData(data)]
+ ]));
+ this.handlers.push(this.resp);
+
+ this.init(d3.select('#topo2-p-summary'));
+
+ this.wss.sendEvent('requestSummary', {});
+ }
+
+ ngOnDestroy() {
+ this.wss.sendEvent('cancelSummary', {});
+ this.wss.unbindHandlers(this.handlers);
+ }
+
+ handleSummaryData(data: SummaryResponse) {
+ this.summaryData = data;
+ this.render();
+ }
+
+ private render() {
+ let endedWithSeparator;
+
+ this.emptyRegions();
+
+ const svg = this.appendToHeader('div')
+ .classed('icon', true)
+ .append('svg');
+ const title = this.appendToHeader('h2');
+ const table = this.appendToBody('table');
+ const tbody = table.append('tbody');
+
+ title.text(this.summaryData.title);
+ this.gs.addGlyph(svg, 'bird', 24, 0, [1, 1]);
+ endedWithSeparator = this.listProps(tbody, this.summaryData);
+ // TODO : review whether we need to use/store end-with-sep state
+ }
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/button.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/button.css
new file mode 100644
index 0000000..1effdbc
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/button.css
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Button Service (layout) -- CSS file
+ */
+
+.button,
+.toggleButton,
+.radioSet {
+ display: inline-block;
+ padding: 0 4px;
+}
+.radioButton {
+ display: inline-block;
+ padding: 0 2px;
+}
+
+.button svg.embeddedIcon,
+.toggleButton svg.embeddedIcon,
+.radioButton svg.embeddedIcon {
+ cursor: pointer;
+}
+.button svg.embeddedIcon .icon rect,
+.toggleButton svg.embeddedIcon .icon rect,
+.radioButton svg.embeddedIcon .icon rect{
+ stroke: none;
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.css
new file mode 100644
index 0000000..449d436
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.css
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/*
+ ONOS GUI -- Topology Toolbar Panel -- CSS file
+ */
+
+
+
+div.tbar-arrow {
+ position: absolute;
+ top: 53%;
+ left: 96%;
+ margin-right: -4%;
+ transform: translate(-50%, -50%);
+ cursor: pointer;
+}
+.safari div.tbar-arrow {
+ top: 46%;
+}
+.firefox div.tbar-arrow {
+ left: 97%;
+ margin-right: -3%;
+}
+
+.toolbar {
+ line-height: 125%;
+}
+.tbar-row {
+ display: inline-block;
+}
+
+.separator {
+ border: 1px solid;
+ margin: 0 4px 0 4px;
+ display: inline-block;
+ height: 23px;
+ width: 0;
+}
+
+#toolbar-topo2-toolbar {
+ padding: 6px;
+}
+
+#toolbar-topo2-toolbar .tbar-row.right {
+ width: 100%;
+}
+
+#toolbar-topo2-toolbar .tbar-row-text {
+ height: 21px;
+ text-align: right;
+ padding: 8px 60px 0 0;
+ font-style: italic;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.html b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.html
new file mode 100644
index 0000000..623c425
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.html
@@ -0,0 +1,77 @@
+<!--
+~ Copyright 2018-present Open Networking Foundation
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="toolbar-topo2-toolbar" class="floatpanel toolbar" [@toolbarState]="on"
+ style="opacity: 1; left: 0px; width: 286px; top: auto; bottom: 10px;">
+ <div class="tbar-arrow" (click)="on =! on">
+ <onos-icon [iconSize]="10" [iconId]="on?'triangleLeft':'triangleRight'"></onos-icon>
+ </div>
+ <div class="tbar-row ctrl-btns">
+ <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-instance-tog" (click)="buttonClicked('instance-tog')">
+ <onos-icon [iconSize]="25" iconId="m_uiAttached" [toolTip]="lionFn('tbtt_tog_instances')" [classes]="['toggleButton', instancesVisible?'selected':'']"></onos-icon>
+ </div>
+ <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-summary-tog" (click)="buttonClicked('summary-tog')">
+ <onos-icon [iconSize]="25" iconId="m_summary" [toolTip]="lionFn('tbtt_tog_summary')" [classes]="['toggleButton', summaryVisible?'selected':'']"></onos-icon>
+ </div>
+ <div class="toggleButton" id="toolbar-topo2-toolbar-details-tog" (click)="buttonClicked('details-tog')">
+ <onos-icon [iconSize]="25" iconId="m_details" [toolTip]="lionFn('tbtt_tog_use_detail')" [classes]="['toggleButton', detailsVisible?'selected':'']"></onos-icon>
+ </div>
+ <div class="separator"></div>
+ <div class="toggleButton" id="toolbar-topo2-toolbar-hosts-tog" (click)="buttonClicked('hosts-tog')">
+ <onos-icon [iconSize]="25" iconId="m_endstation" [toolTip]="lionFn('tbtt_tog_host')" [classes]="['toggleButton', hostsVisible?'selected':'']"></onos-icon>
+ </div>
+ <div class="toggleButton" id="toolbar-topo2-toolbar-offline-tog" (click)="buttonClicked('offline-tog')">
+ <onos-icon [iconSize]="25" iconId="m_switch" [toolTip]="lionFn('tbtt_tog_offline')" classes="toggleButton selected"></onos-icon>
+ </div>
+ <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-ports-tog" (click)="buttonClicked('ports-tog')">
+ <onos-icon [iconSize]="25" iconId="m_ports" [toolTip]="lionFn('tbtt_tog_porthi')" classes="toggleButton selected" [classes]="['toggleButton', portsVisible?'selected':'']"></onos-icon>
+ </div>
+ <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-bkgrnd-tog" (click)="buttonClicked('bkgrnd-tog')">
+ <onos-icon [iconSize]="25" iconId="m_map" [toolTip]="lionFn('tbtt_tog_map')" classes="toggleButton selected" [classes]="['toggleButton', backgroundVisible?'selected':'']"></onos-icon>
+ </div>
+ <div class="toggleButton" id="toolbar-topo2-toolbar-topo2-bkgrnd-sel" (click)="buttonClicked('bkgrnd-sel')">
+ <onos-icon [iconSize]="25" iconId="m_selectMap" [toolTip]="lionFn('tbtt_sel_map')" classes="button"></onos-icon>
+ </div>
+ </div>
+ <br>
+ <div class="tbar-row">
+ <div class="button" id="toolbar-topo2-toolbar-topo2-cycleLabels-btn" (click)="buttonClicked('cycleLabels-btn')">
+ <onos-icon [iconSize]="25" iconId="m_cycleLabels" [toolTip]="lionFn('tbtt_cyc_dev_labs')" classes="button"></onos-icon>
+ </div>
+ <div class="button" id="toolbar-topo2-toolbar-topo2-resetZoom-btn" (click)="buttonClicked('resetZoom-btn')">
+ <onos-icon [iconSize]="25" iconId="m_resetZoom" [toolTip]="lionFn('tbtt_reset_zoom')" classes="button"></onos-icon>
+ </div>
+ <div class="separator"></div>
+ <div class="button" id="toolbar-topo2-toolbar-topo2-eqMaster-btn" (click)="buttonClicked('eqMaster-btn')">
+ <onos-icon [iconSize]="25" iconId="m_eqMaster" [toolTip]="lionFn('tbtt_eq_master')" classes="button"></onos-icon>
+ </div>
+ <div class="separator"></div>
+ <div class="radioSet" id="toolbar-topo2-traffic">
+ <div class="radioButton selected" id="toolbar-topo2-cancel-traffic" (click)="buttonClicked('cancel-traffic')">
+ <onos-icon [iconSize]="25" iconId="m_unknown" [toolTip]="lionFn('tr_btn_cancel_monitoring')" classes="radioButton selected"></onos-icon>
+ </div>
+ <div class="radioButton" id="toolbar-topo2-all-traffic" (click)="buttonClicked('all-traffic')">
+ <onos-icon [iconSize]="25" iconId="m_allTraffic" [toolTip]="lionFn('tr_btn_show_related_traffic')" classes="radioButton selected"></onos-icon>
+ </div>
+ </div>
+ <div class="separator"></div>
+ <div class="button" id="toolbar-topo2-toolbar-topo2-quickhelp" (click)="buttonClicked('quickhelp-btn')">
+ <onos-icon [iconSize]="25" iconId="query" [toolTip]="lionFn('qh_title')" classes="button"></onos-icon>
+ </div>
+ <div class="button" id="toolbar-topo2-toolbar-topo2-cycleGrid-btn" (click)="buttonClicked('cycleGridDisplay-btn')">
+ <onos-icon [iconSize]="25" iconId="m_cycleGridDisplay" [toolTip]="lionFn('tbtt_cyc_grid_display')" classes="button"></onos-icon>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.spec.ts
new file mode 100644
index 0000000..91c1de4
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.spec.ts
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute, Params } from '@angular/router';
+import { of } from 'rxjs';
+import { ToolbarComponent } from './toolbar.component';
+
+import {
+ FnService, LionService,
+ LogService, IconComponent
+} from 'gui2-fw-lib';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
+ }
+}
+
+/**
+ * ONOS GUI -- Topology View Topology Panel-- Unit Tests
+ */
+describe('ToolbarComponent', () => {
+ let fs: FnService;
+ let ar: MockActivatedRoute;
+ let windowMock: Window;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
+ let component: ToolbarComponent;
+ let fixture: ComponentFixture<ToolbarComponent>;
+
+ const bundleObj = {
+ 'core.view.Topo': {
+ test: 'test1'
+ }
+ };
+ const mockLion = (key) => {
+ return bundleObj[key] || '%' + key + '%';
+ };
+
+ beforeEach(async(() => {
+ const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+ ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+ windowMock = <any>{
+ location: <any>{
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true' },
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, windowMock);
+ TestBed.configureTestingModule({
+ imports: [ BrowserAnimationsModule ],
+ declarations: [ ToolbarComponent, IconComponent ],
+ providers: [
+ { provide: FnService, useValue: fs },
+ { provide: LogService, useValue: logSpy },
+ {
+ provide: LionService, useFactory: (() => {
+ return {
+ bundle: ((bundleId) => mockLion),
+ ubercache: new Array(),
+ loadCbs: new Map<string, () => void>([])
+ };
+ })
+ },
+ { provide: 'Window', useValue: windowMock },
+ ]
+ })
+ .compileComponents();
+ logServiceSpy = TestBed.get(LogService);
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ToolbarComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.ts
new file mode 100644
index 0000000..0615530
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.component.ts
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {
+ LogService,
+ LoadingService,
+ FnService,
+ PanelBaseImpl, LionService
+} from 'gui2-fw-lib';
+
+import {animate, state, style, transition, trigger} from '@angular/animations';
+
+export const INSTANCE_TOGGLE = 'instance-tog';
+export const SUMMARY_TOGGLE = 'summary-tog';
+export const DETAILS_TOGGLE = 'details-tog';
+export const HOSTS_TOGGLE = 'hosts-tog';
+export const OFFLINE_TOGGLE = 'offline-tog';
+export const PORTS_TOGGLE = 'ports-tog';
+export const BKGRND_TOGGLE = 'bkgrnd-tog';
+export const BKGRND_SELECT = 'bkgrnd-sel';
+export const CYCLELABELS_BTN = 'cycleLabels-btn';
+export const CYCLEHOSTLABEL_BTN = 'cycleHostLabel-btn';
+export const CYCLEGRIDDISPLAY_BTN = 'cycleGridDisplay-btn';
+export const RESETZOOM_BTN = 'resetZoom-btn';
+export const EQMASTER_BTN = 'eqMaster-btn';
+export const CANCEL_TRAFFIC = 'cancel-traffic';
+export const ALL_TRAFFIC = 'all-traffic';
+export const QUICKHELP_BTN = 'quickhelp-btn';
+
+
+/*
+ ONOS GUI -- Topology Toolbar Module.
+ Defines modeling of ONOS toolbar.
+ */
+@Component({
+ selector: 'onos-toolbar',
+ templateUrl: './toolbar.component.html',
+ styleUrls: [
+ './toolbar.component.css', './toolbar.theme.css',
+ '../../topology.common.css',
+ '../../../../fw/widget/panel.css', '../../../../fw/widget/panel-theme.css',
+ './button.css'
+ ],
+ animations: [
+ trigger('toolbarState', [
+ state('true', style({
+ transform: 'translateX(0%)',
+ // opacity: '1.0'
+ })),
+ state('false', style({
+ transform: 'translateX(-93%)',
+ // opacity: '0.0'
+ })),
+ transition('0 => 1', animate('500ms ease-in')),
+ transition('1 => 0', animate('500ms ease-out'))
+ ])
+ ]
+})
+export class ToolbarComponent extends PanelBaseImpl {
+ @Input() on: boolean = false; // Override the parent class attribute
+ // deferred localization strings
+ lionFn; // Function
+ // Used to drive the display of the hosts icon - there is also another such variable on the forcesvg
+ @Input() hostsVisible: boolean = false;
+ @Input() instancesVisible: boolean = true;
+ @Input() summaryVisible: boolean = true;
+ @Input() detailsVisible: boolean = true;
+ @Input() backgroundVisible: boolean = false;
+ @Input() portsVisible: boolean = true;
+
+ @Output() buttonEvent = new EventEmitter<string>();
+
+ constructor(
+ protected fs: FnService,
+ protected log: LogService,
+ protected ls: LoadingService,
+ private lion: LionService
+ ) {
+ super(fs, ls, log);
+
+ if (this.lion.ubercache.length === 0) {
+ this.lionFn = this.dummyLion;
+ this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
+ } else {
+ this.doLion();
+ }
+
+ this.log.debug('ToolbarComponent constructed');
+ }
+
+ /**
+ * Read the LION bundle for Toolbar and set up the lionFn
+ */
+ doLion() {
+ this.lionFn = this.lion.bundle('core.view.Topo');
+ }
+
+ /**
+ * As buttons are clicked on the toolbar, emit events up to the parent
+ *
+ * The toggling of the input variables here is in addition to the control
+ * of these input variables from the parent. This is so that this component
+ * may be reused and is not dependent on a particular parent implementation
+ * to work
+ * @param name The name of button clicked.
+ */
+ buttonClicked(name: string): void {
+ switch (name) {
+ case HOSTS_TOGGLE:
+ this.hostsVisible = !this.hostsVisible;
+ break;
+ case INSTANCE_TOGGLE:
+ this.instancesVisible = !this.instancesVisible;
+ break;
+ case SUMMARY_TOGGLE:
+ this.summaryVisible = !this.summaryVisible;
+ break;
+ case DETAILS_TOGGLE:
+ this.detailsVisible = !this.detailsVisible;
+ break;
+ case BKGRND_TOGGLE:
+ this.backgroundVisible = !this.backgroundVisible;
+ break;
+ default:
+ }
+ // Send a message up to let TopologyComponent know of the event
+ this.buttonEvent.emit(name);
+ }
+}
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.theme.css b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.theme.css
new file mode 100644
index 0000000..7933ee6
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/toolbar/toolbar.theme.css
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * ONOS GUI -- Topology Toolbar Panel -- Theme CSS file
+ */
+.tbar-arrow svg.embeddedIcon .icon rect {
+ stroke: none;
+}
+
+.tbar-arrow svg.embeddedIcon .icon .glyph {
+ fill: #838383;
+}
+
+.tbar-arrow svg.embeddedIcon .icon rect {
+ fill: none;
+}
+
+.separator {
+ border-color: #ddd;
+}
+
+/* ========== DARK Theme ========== */
+
+.dark .tbar-arrow svg.embeddedIcon .icon .glyph {
+ fill: #B2B2B2;
+}
+
+.dark .tbar-arrow svg.embeddedIcon .icon rect {
+ fill: none;
+}
+
+.dark .separator {
+ border-color: #454545;
+}
+
+.dark #toolbar-topo2-toolbar .tbar-row.right {
+ color: #666;
+}
\ No newline at end of file
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/topopanel.base.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/topopanel.base.ts
new file mode 100644
index 0000000..48aa2ed
--- /dev/null
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/topopanel.base.ts
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {
+ FnService,
+ LoadingService,
+ LogService,
+ PanelBaseImpl
+} from 'gui2-fw-lib';
+
+/**
+ * Base model of panel view - implemented by Topology Panel components
+ */
+export abstract class TopoPanelBaseImpl extends PanelBaseImpl {
+
+ protected header: any;
+ protected body: any;
+ protected footer: any;
+
+ protected constructor(
+ protected fs: FnService,
+ protected ls: LoadingService,
+ protected log: LogService,
+ protected id: string
+ ) {
+ super(fs, ls, log);
+ }
+
+ protected init(el: any) {
+ this.header = el.append('div').classed('header', true);
+ this.body = el.append('div').classed('body', true);
+ this.footer = el.append('div').classed('footer', true);
+ }
+
+ /**
+ * Decode lists of props sent back through Web Socket
+ *
+ * Means that panels do not have to know property names in advance
+ * Driven by PropertyPanel on Server side
+ */
+ listProps(el, data) {
+ let sepLast: boolean = false;
+
+ // note: track whether we end with a separator or not...
+ data.propOrder.forEach((p) => {
+ if (p === '-') {
+ this.addSep(el);
+ sepLast = true;
+ } else {
+ this.addProp(el, data.propLabels[p], data.propValues[p]);
+ sepLast = false;
+ }
+ });
+ return sepLast;
+ }
+
+ addProp(el, label, value) {
+ const tr = el.append('tr');
+ let lab;
+
+ if (typeof label === 'string') {
+ lab = label.replace(/_/g, ' ');
+ } else {
+ lab = label;
+ }
+
+ function addCell(cls, txt) {
+ tr.append('td').attr('class', cls).text(txt);
+ }
+
+ addCell('label', lab + ' :');
+ addCell('value', value);
+ }
+
+ addSep(el) {
+ el.append('tr').append('td').attr('colspan', 2).append('hr');
+ }
+
+ appendToHeader(x) {
+ return this.header.append(x);
+ }
+
+ appendToBody(x) {
+ return this.body.append(x);
+ }
+
+ appendToFooter(x) {
+ return this.footer.append(x);
+ }
+
+ emptyRegions() {
+ this.header.selectAll('*').remove();
+ this.body.selectAll('*').remove();
+ this.footer.selectAll('*').remove();
+ }
+
+}