First part of migrating Topo2 to GUI2

Change-Id: I316dd34cba161688e01dfb7b340bff5f2c3c57d4
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
new file mode 100644
index 0000000..5ed744b
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
@@ -0,0 +1,335 @@
+/*
+ * 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, OnInit, ViewChild } from '@angular/core';
+import {
+    FnService,
+    KeysService, KeysToken,
+    LogService, PrefsService,
+    SvgUtilService, WebSocketService
+} from 'gui2-fw-lib';
+import {InstanceComponent} from '../panel/instance/instance.component';
+import {SummaryComponent} from '../panel/summary/summary.component';
+import {DetailsComponent} from '../panel/details/details.component';
+
+/**
+ * ONOS GUI Topology View
+ *
+ * This Topology View component is the top level component in a hierarchy that
+ * comprises the whole Topology View
+ *
+ * There are three main parts (panels, graphical and breadcrumbs)
+ * The panel hierarchy
+ * |-- Instances Panel (shows ONOS instances)
+ * |-- Summary Panel (summary of ONOS)
+ * |-- Toolbar Panel (the toolbar)
+ * |-- Details Panel (when a node is selected in the Force graphical view (see below))
+ *
+ * The graphical hierarchy contains
+ * Topology (this)
+ *  |-- No Devices Connected (only of there are no nodes to show)
+ *  |-- Zoom Layer (everything beneath this can be zoomed and panned)
+ *      |-- Background (container for any backgrounds - can be toggled on and off)
+ *          |-- Map
+ *      |-- Forces (all of the nodes and links laid out by a d3.force simulation)
+ *
+ * The breadcrumbs
+ * |-- Breadcrumb (in region view a way of navigating back up through regions)
+ */
+@Component({
+  selector: 'onos-topology',
+  templateUrl: './topology.component.html',
+  styleUrls: ['./topology.component.css']
+})
+export class TopologyComponent implements OnInit {
+    @ViewChild(InstanceComponent) instance: InstanceComponent;
+    @ViewChild(SummaryComponent) summary: SummaryComponent;
+    @ViewChild(DetailsComponent) details: DetailsComponent;
+
+    flashMsg: string = '';
+    prefsState = {};
+    svg: any;
+    hostLabelIdx: number = 1;
+
+    constructor(
+        protected log: LogService,
+        protected fs: FnService,
+        protected ks: KeysService,
+        protected sus: SvgUtilService,
+        protected ps: PrefsService,
+        protected wss: WebSocketService
+    ) {
+
+        this.log.debug('Topology component constructed');
+    }
+
+    ngOnInit() {
+        this.bindCommands();
+        this.log.debug('Topology component initialized');
+    }
+
+    actionMap() {
+        return {
+            L: [() => {this.cycleDeviceLabels(); }, 'Cycle device labels'],
+            B: [(token) => {this.toggleBackground(token); }, 'Toggle background'],
+            D: [(token) => {this.toggleDetails(token); }, 'Toggle details panel'],
+            I: [(token) => {this.toggleInstancePanel(token); }, 'Toggle ONOS Instance Panel'],
+            O: [() => {this.toggleSummary(); }, 'Toggle the Summary Panel'],
+            R: [() => {this.resetZoom(); }, 'Reset pan / zoom'],
+            P: [(token) => {this.togglePorts(token); }, 'Toggle Port Highlighting'],
+            E: [() => {this.equalizeMasters(); }, 'Equalize mastership roles'],
+            X: [() => {this.resetNodeLocation(); }, 'Reset Node Location'],
+            U: [() => {this.unpinNode(); }, 'Unpin node (mouse over)'],
+            H: [() => {this.toggleHosts(); }, 'Toggle host visibility'],
+            M: [() => {this.toggleOfflineDevices(); }, 'Toggle offline visibility'],
+            dot: [() => {this.toggleToolbar(); }, 'Toggle Toolbar'],
+            'shift-L': [() => {this.cycleHostLabels(); }, 'Cycle host labels'],
+
+            // -- instance color palette debug
+            9: function () {
+                this.sus.cat7().testCard(this.svg);
+            },
+
+            esc: this.handleEscape,
+
+            // TODO update after adding in Background Service
+            // topology overlay selections
+            // F1: function () { t2tbs.fnKey(0); },
+            // F2: function () { t2tbs.fnKey(1); },
+            // F3: function () { t2tbs.fnKey(2); },
+            // F4: function () { t2tbs.fnKey(3); },
+            // F5: function () { t2tbs.fnKey(4); },
+            //
+            // _keyListener: t2tbs.keyListener.bind(t2tbs),
+
+            _helpFormat: [
+                ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
+                ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
+                [], // this column reserved for overlay actions
+            ],
+        };
+    }
+
+
+    bindCommands(additional?: any) {
+
+        const am = this.actionMap();
+        const add = this.fs.isO(additional);
+
+        // TODO: Reimplement when we have a use case
+        // if (add) {
+        //     _.each(add, function (value, key) {
+        //         // filter out meta properties (e.g. _keyOrder)
+        //         if (!(_.startsWith(key, '_'))) {
+        //             // don't allow re-definition of existing key bindings
+        //             if (am[key]) {
+        //                 this.log.warn('keybind: ' + key + ' already exists');
+        //             } else {
+        //                 am[key] = [value.cb, value.tt];
+        //             }
+        //         }
+        //     });
+        // }
+
+        this.ks.keyBindings(am);
+
+        this.ks.gestureNotes([
+            ['click', 'Select the item and show details'],
+            ['shift-click', 'Toggle selection state'],
+            ['drag', 'Reposition (and pin) device / host'],
+            ['cmd-scroll', 'Zoom in / out'],
+            ['cmd-drag', 'Pan'],
+        ]);
+    }
+
+    handleEscape() {
+
+        if (false) {
+            // TODO: Cancel show mastership
+            // TODO: Cancel Active overlay
+            // TODO: Reinstate with components
+        } else {
+            this.log.debug('Handling escape');
+            // } else if (t2rs.deselectAllNodes()) {
+            //     // else if we have node selections, deselect them all
+            //     // (work already done)
+            // } else if (t2rs.deselectLink()) {
+            //     // else if we have a link selection, deselect it
+            //     // (work already done)
+            // } else if (t2is.isVisible()) {
+            //     // If the instance panel is visible, close it
+            //     t2is.toggle();
+            // } else if (t2sp.isVisible()) {
+            //     // If the summary panel is visible, close it
+            //     t2sp.toggle();
+        }
+    }
+
+
+
+    updatePrefsState(what, b) {
+        this.prefsState[what] = b ? 1 : 0;
+        this.ps.setPrefs('topo2_prefs', this.prefsState);
+    }
+
+    deviceLabelFlashMessage(index) {
+        switch (index) {
+            case 0: return 'Hide device labels';
+            case 1: return 'Show friendly device labels';
+            case 2: return 'Show device ID labels';
+        }
+    }
+
+    hostLabelFlashMessage(index) {
+        switch (index) {
+            case 0: return 'Hide host labels';
+            case 1: return 'Show friendly host labels';
+            case 2: return 'Show host IP labels';
+            case 3: return 'Show host MAC Address labels';
+        }
+    }
+
+    protected cycleDeviceLabels() {
+        this.log.debug('Cycling device labels');
+        // TODO: Reinstate with components
+        // let deviceLabelIndex = t2ps.get('dlbls') + 1;
+        // let newDeviceLabelIndex = deviceLabelIndex % 3;
+        //
+        // t2ps.set('dlbls', newDeviceLabelIndex);
+        // t2fs.updateNodes();
+        // flash.flash(deviceLabelFlashMessage(newDeviceLabelIndex));
+    }
+
+    protected cycleHostLabels() {
+        const hostLabelIndex = this.hostLabelIdx + 1;
+        this.hostLabelIdx = hostLabelIndex % 4;
+        this.flashMsg = this.hostLabelFlashMessage(this.hostLabelIdx);
+        this.log.debug('Cycling host labels');
+        // TODO: Reinstate with components
+        // t2ps.set('hlbls', newHostLabelIndex);
+        // t2fs.updateNodes();
+    }
+
+    protected toggleBackground(token: KeysToken) {
+        this.flashMsg = 'Toggling background';
+        this.log.debug('Toggling background', token);
+        // TODO: Reinstate with components
+        // t2bgs.toggle(x);
+    }
+
+    protected toggleDetails(token: KeysToken) {
+        this.flashMsg = 'Toggling details';
+        this.details.togglePanel(() => {});
+        this.log.debug('Toggling details', token);
+    }
+
+    protected toggleInstancePanel(token: KeysToken) {
+        this.flashMsg = 'Toggling instances';
+        this.instance.togglePanel(() => {});
+        this.log.debug('Toggling instances', token);
+        // TODO: Reinstate with components
+        // this.updatePrefsState('insts', t2is.toggle(x));
+    }
+
+    protected toggleSummary() {
+        this.flashMsg = 'Toggling summary';
+        this.summary.togglePanel(() => {});
+    }
+
+    protected resetZoom() {
+        this.log.debug('resetting zoom');
+        // TODO: Reinstate with components
+        // t2bgs.resetZoom();
+        // flash.flash('Pan and zoom reset');
+    }
+
+    protected togglePorts(token: KeysToken) {
+        this.log.debug('Toggling ports');
+        // TODO: Reinstate with components
+        // this.updatePrefsState('porthl', t2vs.togglePortHighlights(x));
+        // t2fs.updateLinks();
+    }
+
+    protected equalizeMasters() {
+        this.wss.sendEvent('equalizeMasters', null);
+
+        this.log.debug('equalizing masters');
+        // TODO: Reinstate with components
+        // flash.flash('Equalizing master roles');
+    }
+
+    protected resetNodeLocation() {
+        this.log.debug('resetting node location');
+        // TODO: Reinstate with components
+        // t2fs.resetNodeLocation();
+        // flash.flash('Reset node locations');
+    }
+
+    protected unpinNode() {
+        this.log.debug('unpinning node');
+        // TODO: Reinstate with components
+        // t2fs.unpin();
+        // flash.flash('Unpin node');
+    }
+
+    protected toggleToolbar() {
+        this.log.debug('toggling toolbar');
+        // TODO: Reinstate with components
+        // t2tbs.toggle();
+    }
+
+    protected actionedFlashed(action, message) {
+        this.log.debug('action flashed');
+        // TODO: Reinstate with components
+        // this.flash.flash(action + ' ' + message);
+    }
+
+    protected toggleHosts() {
+        // this.flashMsg = on ? 'Show': 'Hide', 'Hosts';
+        this.log.debug('toggling hosts');
+        // TODO: Reinstate with components
+        // let on = t2rs.toggleHosts();
+        // this.actionedFlashed(on ? 'Show': 'Hide', 'Hosts');
+    }
+
+    protected toggleOfflineDevices() {
+        this.log.debug('toggling offline devices');
+        // TODO: Reinstate with components
+        // let on = t2rs.toggleOfflineDevices();
+        // this.actionedFlashed(on ? 'Show': 'Hide', 'offline devices');
+    }
+
+    protected notValid(what) {
+        this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
+    }
+
+    getActionEntry(key) {
+        let entry;
+
+        if (!key) {
+            this.notValid('key');
+            return null;
+        }
+
+        entry = this.actionMap()[key];
+
+        if (!entry) {
+            this.notValid('actionMap (' + key + ') entry');
+            return null;
+        }
+        return this.fs.isA(entry) || [entry, ''];
+    }
+
+}