Implemented table building functions
Change-Id: Ie4003080b13725561df22de41ec85f8c3f31c794
diff --git a/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts b/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts
index 880e2bd..d6c37bb 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/layer/dialog.service.ts
@@ -38,4 +38,7 @@
this.log.debug('DialogService constructed');
}
+ createDiv() {
+ }
+
}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts b/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts
index 0d03ad6..3e6d48b 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/layer/loading.service.ts
@@ -33,7 +33,9 @@
*
* Provides a mechanism to start/stop the loading animation, center screen.
*/
-@Injectable()
+@Injectable({
+ providedIn: 'root',
+})
export class LoadingService {
images: any[] = [];
idx = 0;
@@ -127,7 +129,7 @@
}
// return true if start() has been called but not stop()
- waiting() {
+ waiting(): boolean {
return !!this.wait;
}
diff --git a/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts b/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts
index e470ec1..34a8ea9 100644
--- a/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/remote/websocket.service.ts
@@ -90,7 +90,6 @@
* built-in handler for the 'boostrap' event
*/
private bootstrap(data: Bootstrap) {
- this.log.debug('bootstrap data', data);
this.loggedInUser = data.user;
this.clusterNodes = data.clusterNodes;
@@ -332,6 +331,13 @@
return this.url;
}
+ /**
+ * Tell the WebSocket to close - this should call the handleClose() method
+ */
+ closeWebSocket() {
+ this.ws.close();
+ }
+
/**
* Binds the message handlers to their message type (event type) as
@@ -369,12 +375,12 @@
* Unbinds the specified message handlers.
* Expected that the same map will be used, but we only care about keys
*/
- unbindHandlers(handlerMap: Map<string, (data) => void>): void {
- if (this.noHandlersWarn(handlerMap, 'unbindHandlers')) {
+ unbindHandlers(handlerIds: string[]): void {
+ if ( handlerIds.length === 0 ) {
+ this.log.warn('WSS.unbindHandlers(): no event handlers');
return null;
}
-
- for (const [eventId, api] of handlerMap) {
+ for (const eventId of handlerIds) {
this.handlers.delete(eventId);
}
}
@@ -448,4 +454,7 @@
this.lcd = ld;
}
+ isConnected(): boolean {
+ return this.wsUp;
+ }
}
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon.directive.ts b/web/gui2/src/main/webapp/app/fw/svg/icon.directive.ts
deleted file mode 100644
index a8de561..0000000
--- a/web/gui2/src/main/webapp/app/fw/svg/icon.directive.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-import { Directive, ElementRef, Input, OnInit } from '@angular/core';
-import { IconService } from './icon.service';
-import { LogService } from '../../log.service';
-import * as d3 from 'd3';
-
-/**
- * ONOS GUI -- SVG -- Icon Directive
- *
- * TODO: Deprecated - this directive may be removed altogether as it has been
- * rebuilt as IconComponent instead
- */
-@Directive({
- selector: '[onosIcon]'
-})
-export class IconDirective implements OnInit {
- @Input() iconId: string;
- @Input() iconSize = 20;
-
- constructor(
- private el: ElementRef,
- private is: IconService,
- private log: LogService
- ) {
- // Note: iconId is not available until initialization
- this.log.debug('IconDirective constructed');
- }
-
- ngOnInit() {
- const div = d3.select(this.el.nativeElement);
- div.selectAll('*').remove();
- this.is.loadEmbeddedIcon(div, this.iconId, this.iconSize);
- this.log.debug('IconDirective initialized:', this.iconId);
- }
-
-}
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html
index 6a32d87..5be4c19 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.html
@@ -13,9 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50">
- <g class="icon" [ngClass]="iconId">
+<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50" (mouseover)="toolTipDisp = toolTip" (mouseout)="toolTipDisp = undefined">
+ <g class="icon" [ngClass]="classes">
<rect width="50" height="50" rx="5"></rect>
<use width="50" height="50" class="glyph" [attr.href]="iconTag()"></use>
</g>
</svg>
+<!-- I'm fixing class as light as view encapsulation changes how the hirerarchy of css is handled -->
+<p id="tooltip" class="light" *ngIf="toolTip" [ngStyle]="{ 'display': toolTipDisp ? 'inline-block':'none' }">{{ toolTipDisp }}</p>
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts
index 45be81f..c851f47 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.component.ts
@@ -16,7 +16,6 @@
import { Component, OnInit, Input } from '@angular/core';
import { IconService, glyphMapping } from '../icon.service';
import { LogService } from '../../../log.service';
-import * as d3 from 'd3';
/**
* Icon Component
@@ -31,11 +30,20 @@
@Component({
selector: 'onos-icon',
templateUrl: './icon.component.html',
- styleUrls: ['./icon.component.css', './icon.theme.css', './glyph.css', './glyph-theme.css']
+ styleUrls: [
+ './icon.component.css', './icon.theme.css',
+ './glyph.css', './glyph-theme.css',
+ './tooltip.css', './tooltip-theme.css'
+ ]
})
export class IconComponent implements OnInit {
@Input() iconId: string;
- @Input() iconSize = 20;
+ @Input() iconSize: number = 20;
+ @Input() toolTip: string = undefined;
+ @Input() classes: string = undefined;
+
+ // The displayed tooltip - undefined until mouse hovers over, then equals toolTip
+ toolTipDisp: string = undefined;
constructor(
private is: IconService,
@@ -47,7 +55,6 @@
ngOnInit() {
this.is.loadIconDef(this.iconId);
- this.log.debug('IconComponent initialized for ', this.iconId);
}
/**
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css
index 59cf10f..3e6f601 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/icon.theme.css
@@ -40,6 +40,46 @@
fill: #3c3a3a;
}
+/* --- Control Buttons --- */
+
+/* INACTIVE */
+svg.embeddedIcon g.icon use {
+ fill: #e0dfd6;
+}
+/* note: no change for inactive buttons when hovered */
+
+
+/* ACTIVE */
+svg.embeddedIcon g.icon.active use {
+ fill: #939598;
+}
+svg.embeddedIcon g.icon.active:hover use {
+ fill: #ce5b58;
+}
+
+/* CURRENT-VIEW */
+svg.embeddedIcon g.icon.current-view rect {
+ fill: #518ecc;
+}
+svg.embeddedIcon g.icon.current-view use {
+ fill: white;
+}
+
+/* REFRESH */
+svg.embeddedIcon g.icon.refresh use {
+ fill: #cdeff2;
+}
+svg.embeddedIcon g.icon.refresh:hover use {
+ fill: #ce5b58;
+}
+svg.embeddedIcon g.icon.refresh.active use {
+ fill: #009fdb;
+}
+svg.embeddedIcon g.icon.refresh.active:hover use {
+ fill: #ce5b58;
+}
+
+
/* ========== DARK Theme ========== */
.dark div.close-btn svg.embeddedIcon g.icon .glyph {
@@ -61,11 +101,12 @@
.dark table svg.embeddedIcon .icon .glyph {
fill: #9999aa;
}
-
+/*
svg.embeddedIcon g.icon .glyph {
fill: #007dc4;
}
svg.embeddedIcon:hover g.icon .glyph {
fill: #20b2ff;
-}
\ No newline at end of file
+}
+*/
\ No newline at end of file
diff --git a/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip-theme.css b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip-theme.css
new file mode 100644
index 0000000..a703d1b
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip-theme.css
@@ -0,0 +1,30 @@
+/*
+ * 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 -- Tooltip Service (theme) -- CSS file
+ */
+.light#tooltip {
+ background-color: #dbeffc;
+ color: #3c3a3a;
+ border-color: #c7c7c0;
+}
+
+.dark#tooltip {
+ background-color: #3a3a60;
+ border-color: #6c6a6a;
+ color: #c7c7c0;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tooltip.service.ts b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip.css
similarity index 62%
rename from web/gui2/src/main/webapp/app/fw/widget/tooltip.service.ts
rename to web/gui2/src/main/webapp/app/fw/svg/icon/tooltip.css
index 6b08779..74a5443 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/tooltip.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/svg/icon/tooltip.css
@@ -13,21 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Injectable } from '@angular/core';
-import { FnService } from '../util/fn.service';
-import { LogService } from '../../log.service';
-/**
- * ONOS GUI -- Widget -- Tooltip Service
+/*
+ ONOS GUI -- Tooltip Service (layout) -- CSS file
*/
-@Injectable()
-export class TooltipService {
- constructor(
- private fs: FnService,
- private log: LogService,
- ) {
- this.log.debug('TooltipService constructed');
- }
-
+#tooltip {
+ text-align: center;
+ font-size: 80%;
+ border: 1px solid;
+ padding: 5px;
+ position: absolute;
+ z-index: 5000;
+ display: none;
+ pointer-events: none;
}
diff --git a/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts b/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts
index 9688c30..3174263 100644
--- a/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts
+++ b/web/gui2/src/main/webapp/app/fw/svg/svg.module.ts
@@ -25,7 +25,6 @@
import { SpriteService } from './sprite.service';
import { SpriteDataService } from './spritedata.service';
import { SvgUtilService } from './svgutil.service';
-import { IconDirective } from './icon.directive';
import { IconComponent } from './icon/icon.component';
/**
@@ -33,7 +32,6 @@
*/
@NgModule({
exports: [
- IconDirective,
IconComponent
],
imports: [
@@ -41,7 +39,6 @@
UtilModule
],
declarations: [
- IconDirective,
IconComponent
],
providers: [
diff --git a/web/gui2/src/main/webapp/app/fw/util/fn.service.ts b/web/gui2/src/main/webapp/app/fw/util/fn.service.ts
index d355ce9..7b9ea24 100644
--- a/web/gui2/src/main/webapp/app/fw/util/fn.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/util/fn.service.ts
@@ -415,6 +415,35 @@
}
/**
+ * returns true if the two objects have all the same properties
+ */
+ sameObjProps(obj1: Object, obj2: Object): boolean {
+ for (const key in obj1) {
+ if (obj1.hasOwnProperty(key)) {
+ if (!(obj1[key] === obj2[key])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * returns true if the array contains the object
+ * does NOT use strict object reference equality,
+ * instead checks each property individually for equality
+ */
+ containsObj(arr: any[], obj: Object): boolean {
+ const len = arr.length;
+ for (let i = 0; i < len; i++) {
+ if (this.sameObjProps(arr[i], obj)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Return the given string with the first character capitalized.
*/
cap(s: string): string {
diff --git a/web/gui2/src/main/webapp/app/fw/util/lion.service.ts b/web/gui2/src/main/webapp/app/fw/util/lion.service.ts
index 5ab9f65..248fe51 100644
--- a/web/gui2/src/main/webapp/app/fw/util/lion.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/util/lion.service.ts
@@ -34,7 +34,8 @@
})
export class LionService {
- ubercache: any[];
+ ubercache: any[] = [];
+ loadCb; // Function
/**
* Handler for uberlion event from WSS
@@ -50,6 +51,10 @@
this.log.info(' :=> ', p);
}
}
+ if (this.loadCb) {
+ this.log.debug('Calling the load callback');
+ this.loadCb();
+ }
this.log.debug('LION service: uber-lion bundle received:', data);
}
@@ -69,17 +74,15 @@
* returns a function that takes a string and returns a string
*/
bundle(bundleId: string): (string) => string {
- let bundle = this.ubercache[bundleId];
+ let bundleObj = this.ubercache[bundleId];
- if (!bundle) {
+ if (!bundleObj) {
this.log.warn('No lion bundle registered:', bundleId);
- bundle = {};
+ bundleObj = {};
}
- return this.getKey;
- }
-
- getKey(key: string): string {
- return this.bundle[key] || '%' + key + '%';
+ return (key) => {
+ return bundleObj[key] || '%' + key + '%';
+ };
}
}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/button.service.ts b/web/gui2/src/main/webapp/app/fw/widget/button.service.ts
index da6cbab..4e22763 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/button.service.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/button.service.ts
@@ -17,7 +17,6 @@
import { FnService } from '../util/fn.service';
import { IconService } from '../svg/icon.service';
import { LogService } from '../../log.service';
-import { TooltipService } from './tooltip.service';
/**
* ONOS GUI -- Widget -- Button Service
@@ -29,7 +28,6 @@
private is: IconService,
private fs: FnService,
private log: LogService,
- private tts: TooltipService
) {
this.log.debug('ButtonService constructed');
}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/table-theme.css b/web/gui2/src/main/webapp/app/fw/widget/table-theme.css
new file mode 100644
index 0000000..1edfab0
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/table-theme.css
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+/* ------ for summary-list tables (theme) ------ */
+
+.light div.summary-list, .table-header th {
+ background-color: #e5e5e6;
+ color: #3c3a3a;
+}
+
+.light div.summary-list, td {
+ color: #3c3a3a;
+}
+
+.light div.summary-list, tr:nth-child(even) {
+ background-color: #f4f4f4;
+}
+.light div.summary-list, tr:nth-child(odd) {
+ background-color: #fbfbfb;
+}
+
+.light div.summary-list, tr.selected {
+ background-color: #dbeffc !important;
+}
+
+
+.light div.summary-list, tr.data-change {
+ background-color: #FDFFDC;
+}
+
+/* --- Control Buttons --- */
+
+/* INACTIVE */
+.light .ctrl-btns div svg.embeddedIcon g.icon use {
+ fill: #e0dfd6;
+}
+/* note: no change for inactive buttons when hovered */
+
+
+/* ACTIVE */
+.light .ctrl-btns div.active svg.embeddedIcon g.icon use {
+ fill: #939598;
+}
+.light .ctrl-btns div.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+
+/* CURRENT-VIEW */
+.light .ctrl-btns div.current-view svg.embeddedIcon g.icon rect {
+ fill: #518ecc;
+}
+.light .ctrl-btns div.current-view svg.embeddedIcon g.icon use {
+ fill: white;
+}
+
+/* REFRESH */
+.light .ctrl-btns div.refresh svg.embeddedIcon g.icon use {
+ fill: #cdeff2;
+}
+.light .ctrl-btns div.refresh:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+.light .ctrl-btns div.refresh.active svg.embeddedIcon g.icon use {
+ fill: #009fdb;
+}
+.light .ctrl-btns div.refresh.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+
+
+/* ========== DARK Theme ========== */
+
+.dark div.summary-list .table-header td {
+ background-color: #222222;
+ color: #cccccc;
+}
+
+.dark div.summary-list td {
+ /* note: don't put background-color here */
+ color: #cccccc;
+}
+.dark div.summary-list tr.no-data td {
+ background-color: #333333;
+}
+
+.dark div.summary-list tr:nth-child(even) {
+ background-color: #333333;
+}
+.dark div.summary-list tr:nth-child(odd) {
+ background-color: #3a3a3a;
+}
+
+.dark div.summary-list tr.selected {
+ background-color: #304860 !important;
+}
+
+
+.dark div.summary-list tr.data-change {
+ background-color: #423708;
+}
+
+/* --- Control Buttons --- */
+
+/* INACTIVE */
+.dark .ctrl-btns div svg.embeddedIcon g.icon use {
+ fill: #444444;
+}
+/* note: no change for inactive buttons when hovered */
+
+
+/* ACTIVE */
+.dark .ctrl-btns div.active svg.embeddedIcon g.icon use {
+ fill: #939598;
+}
+.dark .ctrl-btns div.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+
+/* CURRENT-VIEW */
+.dark .ctrl-btns div.current-view svg.embeddedIcon g.icon rect {
+ fill: #518ecc;
+}
+.dark .ctrl-btns div.current-view svg.embeddedIcon g.icon use {
+ fill: #dddddd;
+}
+
+/* REFRESH */
+.dark .ctrl-btns div.refresh svg.embeddedIcon g.icon use {
+ fill: #364144;
+}
+.dark .ctrl-btns div.refresh:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
+.dark .ctrl-btns div.refresh.active svg.embeddedIcon g.icon use {
+ fill: #0074a6;
+}
+.dark .ctrl-btns div.refresh.active:hover svg.embeddedIcon g.icon use {
+ fill: #ce5b58;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/table.css b/web/gui2/src/main/webapp/app/fw/widget/table.css
new file mode 100644
index 0000000..3b761f6
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/table.css
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+/* ------ for summary-list tables (layout) ------ */
+
+div.summary-list {
+ margin: 0 20px 16px 10px;
+ font-size: 10pt;
+ border-spacing: 0;
+}
+
+div.summary-list table {
+ border-collapse: collapse;
+ table-layout: fixed;
+ empty-cells: show;
+ margin: 0;
+}
+
+div.summary-list div.table-body {
+ overflow-y: scroll;
+}
+
+div.summary-list div.table-body::-webkit-scrollbar {
+ display: none;
+}
+
+div.summary-list tr.no-data td {
+ text-align: center;
+ font-style: italic;
+}
+
+
+/* highlighting */
+div.summary-list tr {
+ transition: background-color 500ms;
+}
+
+div.summary-list td {
+ padding: 4px;
+ text-align: left;
+ word-wrap: break-word;
+ font-size: 10pt;
+}
+
+div.summary-list td.table-icon {
+ width: 42px;
+ padding-top: 4px;
+ padding-bottom: 0px;
+ padding-left: 4px;
+ text-align: center;
+}
+
+div.summary-list .table-header th {
+ font-weight: bold;
+ font-variant: small-caps;
+ text-transform: uppercase;
+ font-size: 10pt;
+ padding-top: 8px;
+ padding-bottom: 8px;
+ letter-spacing: 0.02em;
+ cursor: pointer;
+}
+
+/* rows are selectable */
+div.summary-list .table-body td {
+ cursor: pointer;
+}
+
+/* Tabular view controls */
+
+div.tabular-header .search {
+ margin: 0 0 10px 10px;
+}
+
+
+div.tabular-header div.ctrl-btns {
+ display: inline-block;
+ float: right;
+ height: 44px;
+ margin-top: 24px;
+ margin-right: 20px;
+ position: absolute;
+ right: 0px;
+}
+
+div.tabular-header div.ctrl-btns div {
+ display: inline-block;
+ cursor: pointer;
+}
+
+div.tabular-header div.ctrl-btns div.separator {
+ width: 0;
+ height: 40px;
+ padding: 0;
+ border-right: 1px solid #c7c7c0;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts b/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
new file mode 100644
index 0000000..807a014
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+import { Injectable } from '@angular/core';
+import { FnService } from '../util/fn.service';
+import { LoadingService } from '../layer/loading.service';
+import { LogService } from '../../log.service';
+import { WebSocketService } from '../remote/websocket.service';
+
+const REFRESH_INTERVAL = 2000;
+
+/**
+ * Base model of table view - implemented by Table components
+ */
+export interface TableBase {
+ annots: TableAnnots;
+ autoRefresh: boolean;
+ autoRefreshTip: string;
+ changedData: any;
+ payloadParams: any;
+ selId: string;
+ sortParams: any;
+ tableData: any[];
+ toggleRefresh(): void;
+ selectCallback(event: any, selRow: any): void;
+ parentSelCb(event: any, selRow: any): void;
+ sortCallback(): void;
+ responseCallback(): void;
+}
+
+interface TableAnnots {
+ noRowsMsg: string;
+}
+
+/**
+ * A model of data returned in a TableResponse
+ *
+ * There is an interface extending from this one in the parent component
+ */
+export interface TableResponse {
+ annots: any;
+ // There will be other parts to the response depending on table type
+ // Expect one called tag+'s' e.g. devices or apps
+}
+
+/**
+ * ONOS GUI -- Widget -- Table Base class
+ */
+export class TableBaseImpl implements TableBase {
+ // attributes from the interface
+ public annots: TableAnnots;
+ autoRefresh: boolean = true;
+ autoRefreshTip: string = 'Toggle auto refresh'; // TODO: get LION string
+ changedData: string[] = [];
+ payloadParams: any;
+ selId: string = undefined;
+ sortParams: any;
+ tableData: any[] = [];
+ toggleRefresh; // Function
+ selectCallback; // Function
+ parentSelCb = null;
+ sortCallback; // Function
+ responseCallback; // Function
+
+ private root: string;
+ private req: string;
+ private resp: string;
+ private refreshPromise: any = null;
+ private handlers: string[] = [];
+
+ constructor(
+ protected fs: FnService,
+ protected ls: LoadingService,
+ protected log: LogService,
+ protected wss: WebSocketService,
+ protected tag: string,
+ protected idKey: string = 'id',
+ protected query: string = '',
+ protected selCb = () => ({}) // Function
+ ) {
+ this.root = tag + 's';
+ this.req = tag + 'DataRequest';
+ this.resp = tag + 'DataResponse';
+
+ this.sortCallback = this.requestTableData;
+ this.selectCallback = this.rowSelectionCb;
+ this.toggleRefresh = () => {
+ this.autoRefresh = !this.autoRefresh;
+ this.autoRefresh ? this.startRefresh() : this.stopRefresh();
+ };
+ }
+
+ init() {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ [this.resp, (data) => this.tableDataResponseCb(data)]
+ ]));
+ this.handlers.push(this.resp);
+
+ this.annots = <TableAnnots>{
+ noRowsMsg: ''
+ };
+
+ // Now send the WebSocket request and make it repeat every 2 seconds
+ this.requestTableData();
+ this.startRefresh();
+
+ this.log.debug('TableBase initialized');
+ }
+
+ destroy() {
+ this.wss.unbindHandlers(this.handlers);
+ this.stopRefresh();
+ this.ls.stop();
+ }
+
+ /**
+ * A callback that executes when the table data that was requested
+ * on WebSocketService arrives.
+ *
+ * Happens every 2 seconds
+ */
+ tableDataResponseCb(data: TableResponse) {
+ this.ls.stop();
+
+ const newTableData: any[] = Array.from(data[this.root]);
+ this.annots.noRowsMsg = data.annots.no_rows_msg;
+
+ // If the onResp() function is set then call it
+ if (this.responseCallback) {
+ this.responseCallback(data);
+ }
+ this.changedData = [];
+
+ // checks if data changed for row flashing
+ if (JSON.stringify(newTableData) !== JSON.stringify(this.tableData)) {
+ this.log.debug('table data has changed');
+ const oldTableData: any[] = this.tableData;
+ this.tableData = [ ...newTableData ]; // ES6 spread syntax
+ // only flash the row if the data already exists
+ if (oldTableData.length > 0) {
+ for (const idx in newTableData) {
+ if (!this.fs.containsObj(oldTableData, newTableData[idx])) {
+ this.changedData.push(newTableData[idx][this.idKey]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Table Data Request
+ */
+ requestTableData() {
+ const p = Object.assign({}, this.sortParams, this.payloadParams, this.query);
+
+ // Allow it to sit in pending events
+ if (this.wss.isConnected()) {
+ if (this.fs.debugOn('table')) {
+ this.log.debug('Table data REQUEST:', this.req, p);
+ }
+ this.wss.sendEvent(this.req, p);
+ this.ls.start();
+ }
+ }
+
+ /**
+ * Row Selected
+ */
+ rowSelectionCb(event: any, selRow: any) {
+ const selId: string = selRow[this.idKey];
+ this.selId = (this.selId === selId) ? undefined : selId;
+ if (this.parentSelCb) {
+ this.log.debug('Parent called on Row', selId, 'selected');
+ this.parentSelCb(event, selRow);
+ }
+ }
+
+ /**
+ * autoRefresh functions
+ */
+ startRefresh() {
+ this.refreshPromise =
+ setInterval(() => {
+ if (!this.ls.waiting()) {
+ if (this.fs.debugOn('table')) {
+ this.log.debug('Refreshing ' + this.root + ' page');
+ }
+ this.requestTableData();
+ }
+ }, REFRESH_INTERVAL);
+ }
+
+ stopRefresh() {
+ if (this.refreshPromise) {
+ clearInterval(this.refreshPromise);
+ this.refreshPromise = null;
+ }
+ }
+
+ isChanged(id: string): boolean {
+ return (this.fs.inArray(id, this.changedData) === -1) ? false : true;
+ }
+}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tablebuilder.service.ts b/web/gui2/src/main/webapp/app/fw/widget/tablebuilder.service.ts
deleted file mode 100644
index 6c804d1..0000000
--- a/web/gui2/src/main/webapp/app/fw/widget/tablebuilder.service.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-import { Injectable } from '@angular/core';
-import { FnService } from '../util/fn.service';
-import { LoadingService } from '../layer/loading.service';
-import { LogService } from '../../log.service';
-import { WebSocketService } from '../remote/websocket.service';
-
-/**
- * ONOS GUI -- Widget -- Table Builder Service
- */
-@Injectable()
-export class TableBuilderService {
-
- constructor(
- private fs: FnService,
- private ls: LoadingService,
- private log: LogService,
- private wss: WebSocketService
- ) {
- this.log.debug('TableBuilderService constructed');
- }
-
-}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tooltip.directive.ts b/web/gui2/src/main/webapp/app/fw/widget/tooltip.directive.ts
deleted file mode 100644
index 92f8ae3..0000000
--- a/web/gui2/src/main/webapp/app/fw/widget/tooltip.directive.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-import { Directive } from '@angular/core';
-import { FnService } from '../util/fn.service';
-import { LogService } from '../../log.service';
-
-/**
- * ONOS GUI -- Widget -- Tooltip Directive
- */
-@Directive({
- selector: '[onosTooltip]'
-})
-export class TooltipDirective {
-
- constructor(
- private fs: FnService,
- private log: LogService
- ) {
- this.log.debug('TooltipDirective constructed');
- }
-
-}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts b/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
index 2e7a0ea..02393a9 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
@@ -23,11 +23,8 @@
import { ButtonService } from './button.service';
import { ChartBuilderService } from './chartbuilder.service';
import { ListService } from './list.service';
-import { TableBuilderService } from './tablebuilder.service';
import { TableDetailService } from './tabledetail.service';
import { ToolbarService } from './toolbar.service';
-import { TooltipService } from './tooltip.service';
-import { TooltipDirective } from './tooltip.directive';
import { SortableHeaderDirective } from './sortableheader.directive';
import { TableResizeDirective } from './tableresize.directive';
import { FlashChangesDirective } from './flashchanges.directive';
@@ -42,7 +39,6 @@
// It's enough to import them in the OnosModule
],
declarations: [
- TooltipDirective,
SortableHeaderDirective,
TableResizeDirective,
FlashChangesDirective
@@ -51,9 +47,7 @@
ButtonService,
ChartBuilderService,
ListService,
- TableBuilderService,
TableDetailService,
- TooltipService,
ToolbarService
]
})