FM GUI as an NPM library for GUI 2

* added dynamic loading of external modules
* new commands on Alarm to allow create/updating/delete
* new fields in alarm gui

Change-Id: I9a7f4d665618a7949bb02039374974dabf6e5363
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.html b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.html
index a8e2489..098d83a 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.html
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.html
@@ -14,9 +14,9 @@
 ~ limitations under the License.
 -->
 <div id="app-dialog" class="floatpanel dialog" [@confirmDlgState]="message!==''">
-    <h3> {{ lionFn('dlg_confirm_action') }} </h3>
+    <h3> {{ title }} </h3>
     <p>{{ message }}</p>
     <p *ngIf="warning" class="warning strong">{{ warning }}</p>
     <div tabindex="10" class="dialog-button" (click)="choice(true)">OK</div>
     <div tabindex="11" class="dialog-button" (click)="choice(false)">Cancel</div>
-</div>
\ No newline at end of file
+</div>
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.spec.ts b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.spec.ts
index f4e76e0..9692e3a 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.spec.ts
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.spec.ts
@@ -32,7 +32,8 @@
     let fixture: ComponentFixture<ConfirmComponent>;
     const bundleObj = {
         'core.view.App': {
-            test: 'test1'
+            test: 'test1',
+            dlg_confirm_action: 'Confirm'
         }
     };
     const mockLion = (key) => {
@@ -62,6 +63,9 @@
     beforeEach(() => {
         fixture = TestBed.createComponent(ConfirmComponent);
         component = fixture.debugElement.componentInstance;
+        component.title = 'Confirm';
+        component.message = 'A message';
+        component.warning = 'A warning';
         fixture.detectChanges();
     });
 
@@ -73,7 +77,7 @@
         const appDe: DebugElement = fixture.debugElement;
         const divDe = appDe.query(By.css('div#app-dialog h3'));
         const div: HTMLElement = divDe.nativeElement;
-        expect(div.textContent).toEqual(' %dlg_confirm_action% ');
+        expect(div.textContent).toEqual(' Confirm ');
     });
 
     it('should have a div.dialog-button inside a div#app-dialog', () => {
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.ts b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.ts
index fca6419..7533a7e 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.ts
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/layer/confirm/confirm.component.ts
@@ -61,6 +61,7 @@
 
     @Input() message: string;
     @Input() warning: string;
+    @Input() title: string;
     @Output() chosen: EventEmitter<boolean> = new EventEmitter();
 
     constructor(
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.html b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.html
index 72242ba..2d4e606 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.html
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.html
@@ -24,6 +24,7 @@
         <img src="data/img/nav-menu-mojo.png"/>
     </span>
     <img class="logo" src="data/img/masthead-logo-mojo.png">
+    <onos-confirm title="{{ lionFn('ui_ok_to_update') }}" message="{{ confirmMessage }}" warning="{{ strongWarning }}" (chosen)="dOk($event)"></onos-confirm>
     <div id="mast-right">
         <nav>
             <div class="dropdown-parent">
@@ -40,4 +41,4 @@
         </nav>
 
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.spec.ts b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.spec.ts
index de42b62..fe424e7 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.spec.ts
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.spec.ts
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { RouterTestingModule } from '@angular/router/testing';
 import { DebugElement } from '@angular/core';
 import { By } from '@angular/platform-browser';
 
@@ -21,6 +23,7 @@
 import { ConsoleLoggerService } from '../../consolelogger.service';
 import { MastComponent } from './mast.component';
 import { IconComponent } from '../../svg/icon/icon.component';
+import { ConfirmComponent } from '../../layer/confirm/confirm.component';
 import { LionService } from '../../util/lion.service';
 import { IconService } from '../../svg/icon.service';
 import { NavService } from '../../nav/nav.service';
@@ -73,7 +76,8 @@
         };
 
         TestBed.configureTestingModule({
-            declarations: [ MastComponent, IconComponent ],
+            imports: [ BrowserAnimationsModule, RouterTestingModule ],
+            declarations: [ MastComponent, IconComponent, ConfirmComponent ],
             providers: [
                 { provide: LogService, useValue: log },
                 { provide: NavService, useClass: MockNavService },
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.ts b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.ts
index e6a927c..e8e5fec 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.ts
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/mast/mast/mast.component.ts
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, Input, OnInit, OnDestroy, Inject } from '@angular/core';
+import { Component, Input, OnInit, OnDestroy, Inject, NgZone } from '@angular/core';
+import { Router } from '@angular/router';
 import { LionService } from '../../util/lion.service';
 import { LogService } from '../../log.service';
 import { NavService } from '../../nav/nav.service';
+import { WebSocketService } from '../../remote/websocket.service';
 
 /**
  * ONOS GUI -- Masthead Component
@@ -31,11 +33,16 @@
 
     lionFn; // Function
     viewMap = new Map<string, string>([]); // A map of app names
+    confirmMessage: string = '';
+    strongWarning: string = '';
 
     constructor(
         private lion: LionService,
         private log: LogService,
         public ns: NavService,
+        private wss: WebSocketService,
+        private router: Router,
+        private zone: NgZone,
         @Inject('Window') private window: any,
     ) {
         this.viewMap.set('apps', 'https://wiki.onosproject.org/display/ONOS/GUI+Application+View');
@@ -51,6 +58,11 @@
         } else {
             this.doLion();
         }
+
+        this.wss.bindHandlers(new Map<string, (data) => void>([
+            ['guiRemoved', (data) => this.triggerRefresh(data, false) ],
+            ['guiAdded', (data) => this.triggerRefresh(data, true) ]
+        ]));
         this.log.debug('MastComponent initialized');
     }
 
@@ -89,4 +101,28 @@
         }
         this.window.open(helpUrl);
     }
+
+    triggerRefresh(data: any, added: boolean): void {
+        this.confirmMessage = this.lionFn(added ? 'uicomp_added' : 'uicomp_removed');
+        this.log.debug('Refresh has been triggered - item', added ? 'added' : 'removed', ' - ', data);
+    }
+
+    /**
+     * Callback when the Confirm dialog is shown and a choice is made
+     */
+    dOk(choice: boolean) {
+        if (choice) {
+            this.ns.getUiViews();
+            this.router.navigate(['/']);
+            this.zone.runOutsideAngular(() => {
+                location.reload();
+            });
+            this.log.debug('Refresh confirmed'); // Will not be printed if page reloads
+
+        } else {
+            this.log.debug('Refresh cancelled');
+        }
+        this.confirmMessage = '';
+        this.strongWarning = '';
+    }
 }
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/nav/nav.service.ts b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/nav/nav.service.ts
index 22f0549..a24f242 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/nav/nav.service.ts
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/nav/nav.service.ts
@@ -14,9 +14,17 @@
  * limitations under the License.
  */
 import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
 import { FnService } from '../util/fn.service';
 import { LogService } from '../log.service';
 
+export interface UiView {
+    id: string;
+    icon: string;
+    cat: string;
+    label: string;
+}
+
 /**
  * ONOS GUI -- Navigation Service
  */
@@ -26,9 +34,15 @@
 export class NavService {
     public showNav = false;
 
+    uiPlatformViews = new Array<UiView>();
+    uiNetworkViews = new Array<UiView>();
+    uiOtherViews = new Array<UiView>();
+    uiHiddenViews = new Array<UiView>();
+
     constructor(
         private _fn_: FnService,
-        private log: LogService
+        private log: LogService,
+        private httpClient: HttpClient
     ) {
         this.log.debug('NavService constructed');
     }
@@ -49,4 +63,24 @@
         }
     }
 
+    getUiViews() {
+        this.uiPlatformViews = new Array<UiView>();
+        this.uiNetworkViews = new Array<UiView>();
+        this.uiOtherViews = new Array<UiView>();
+        this.uiHiddenViews = new Array<UiView>();
+        this.httpClient.get('rs/nav/uiextensions').subscribe((v: UiView[]) => {
+            v.forEach((uiView: UiView) => {
+                if (uiView.cat === 'PLATFORM') {
+                    this.uiPlatformViews.push(uiView);
+                } else if (uiView.cat === 'NETWORK') {
+                    this.uiNetworkViews.push(uiView);
+                } else if (uiView.cat === 'HIDDEN') {
+                    this.uiHiddenViews.push(uiView);
+                } else {
+                    this.uiOtherViews.push(uiView);
+                }
+            });
+        });
+    }
+
 }
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.base.ts b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.base.ts
index 591aa77..41ce239 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.base.ts
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.base.ts
@@ -77,11 +77,11 @@
  */
 export abstract class TableBaseImpl {
     // attributes from the interface
-    protected annots: TableAnnots;
+    public annots: TableAnnots;
     protected changedData: string[] = [];
     protected payloadParams: PayloadParams;
     protected sortParams: SortParams;
-    protected selectCallback; // Function
+    public selectCallback; // Function
     protected parentSelCb = null;
     protected responseCallback; // Function
     selId: string = undefined;
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.css b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.css
index 6b78988..82cfb20 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.css
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/table.css
@@ -63,7 +63,7 @@
     text-align: center;
 }
 
-div.summary-list .table-header th {
+div.summary-list .table-header td {
     font-weight: bold;
     font-variant: small-caps;
     text-transform: uppercase;
diff --git a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/tableresize.directive.ts b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/tableresize.directive.ts
index 150d3a7..0678583 100644
--- a/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/tableresize.directive.ts
+++ b/web/gui2-fw-lib/projects/gui2-fw-lib/src/lib/widget/tableresize.directive.ts
@@ -53,7 +53,7 @@
         this.adjustTable(tables, wsz.width, wsz.height);
     }
 
-    @HostListener('window:resize', ['event'])
+    @HostListener('window:resize', ['$event.target'])
     onResize(event: any) {
         this.windowSize(this.tables);
         return {