Topo2: Reset node position and unpin
Refactored NodeModel
Added class to the surrounding rect for selected class
Renamed Panels to avoid conflict with classic topo
Topo2: Details Panel for single device selection
Topo2: Added Equalize Masters keyboard shortcut
Topo2: Toggle Link Port highlighting
Topo2: Node Labels was returning empty string
if friendly name was null
Topo2: Reset map zoom and panning
Change-Id: I0a949b2f8205e1abcfcac5aaec65c18d76e77cff
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2DeviceDetailsPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2DeviceDetailsPanel.js
new file mode 100644
index 0000000..9673600
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2DeviceDetailsPanel.js
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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 View Module.
+ Module that displays the details panel for selected nodes
+ */
+
+(function () {
+ 'use strict';
+
+ // Injected Services
+ var Panel, gs, wss, flash, bs, fs, ns;
+
+ // Internal State
+ var detailsPanel;
+
+ // configuration
+ var id = 'topo2-p-detail',
+ className = 'topo-p',
+ panelOpts = {
+ width: 260 // summary and detail panel width
+ },
+ handlerMap = {
+ 'showDetails': showDetails
+ };
+
+ var coreButtons = {
+ showDeviceView: {
+ gid: 'switch',
+ tt: 'Show Device View',
+ path: 'device'
+ },
+ showFlowView: {
+ gid: 'flowTable',
+ tt: 'Show Flow View for this Device',
+ path: 'flow'
+ },
+ showPortView: {
+ gid: 'portTable',
+ tt: 'Show Port View for this Device',
+ path: 'port'
+ },
+ showGroupView: {
+ gid: 'groupTable',
+ tt: 'Show Group View for this Device',
+ path: 'group'
+ },
+ showMeterView: {
+ gid: 'meterTable',
+ tt: 'Show Meter View for this Device',
+ path: 'meter'
+ }
+ };
+
+ function init() {
+
+ bindHandlers();
+
+ var options = angular.extend({}, panelOpts, {
+ class: className
+ });
+
+ detailsPanel = new Panel(id, options);
+ detailsPanel.p.classed(className, true);
+ }
+
+ function addProp(tbody, label, value) {
+ var tr = tbody.append('tr'),
+ lab;
+ if (typeof label === 'string') {
+ lab = label.replace(/_/g, ' ');
+ } else {
+ lab = label;
+ }
+
+ function addCell(cls, txt) {
+ tr.append('td').attr('class', cls).html(txt);
+ }
+ addCell('label', lab + ' :');
+ addCell('value', value);
+ }
+
+ function addSep(tbody) {
+ tbody.append('tr').append('td').attr('colspan', 2).append('hr');
+ }
+
+ function listProps(tbody, data) {
+ data.propOrder.forEach(function (p) {
+ if (p === '-') {
+ addSep(tbody);
+ } else {
+ addProp(tbody, p, data.props[p]);
+ }
+ });
+ }
+
+ function addBtnFooter() {
+ detailsPanel.appendToFooter('hr');
+ detailsPanel.appendToFooter('div').classed('actionBtns', true);
+ }
+
+ function addAction(o) {
+ var btnDiv = d3.select('#' + id)
+ .select('.actionBtns')
+ .append('div')
+ .classed('actionBtn', true);
+ bs.button(btnDiv, id + '-' + o.id, o.gid, o.cb, o.tt);
+ }
+
+ function installButtons(buttons, data, devId) {
+ buttons.forEach(function (id) {
+ var btn = coreButtons[id],
+ gid = btn && btn.gid,
+ tt = btn && btn.tt,
+ path = btn && btn.path;
+
+ if (btn) {
+ addAction({
+ id: 'core-' + id,
+ gid: gid,
+ tt: tt,
+ cb: function () { ns.navTo(path, { devId: devId }); }
+ });
+ }
+ // else if (btn = _getButtonDef(id, data)) {
+ // addAction(btn);
+ // }
+ });
+ }
+
+ function renderSingle(data) {
+
+ detailsPanel.emptyRegions();
+
+ var svg = detailsPanel.appendToHeader('div')
+ .classed('icon clickable', true)
+ .append('svg'),
+ title = detailsPanel.appendToHeader('h2')
+ .classed('clickable', true),
+ table = detailsPanel.appendToBody('table'),
+ tbody = table.append('tbody'),
+ navFn;
+
+ gs.addGlyph(svg, (data.type || 'unknown'), 26);
+ title.text(data.title);
+
+ // // only add navigation when displaying a device
+ // if (isDevice[data.type]) {
+ // navFn = function () {
+ // ns.navTo(devPath, { devId: data.id });
+ // };
+ //
+ // svg.on('click', navFn);
+ // title.on('click', navFn);
+ // }
+
+ listProps(tbody, data);
+ addBtnFooter();
+ }
+
+
+ function bindHandlers() {
+ wss.bindHandlers(handlerMap);
+ }
+
+ function updateDetails(id, nodeType) {
+ wss.sendEvent('requestDetails', {
+ id: id,
+ class: nodeType
+ });
+ }
+
+ function showDetails(data) {
+ var buttons = fs.isA(data.buttons) || [];
+ renderSingle(data);
+ installButtons(buttons, data, data.id);
+ }
+
+ function toggle() {
+ var on = detailsPanel.p.toggle(),
+ verb = on ? 'Show' : 'Hide';
+ flash.flash(verb + ' Summary Panel');
+ }
+
+ function show() {
+ detailsPanel.p.show();
+ }
+
+ function hide() {
+ detailsPanel.p.hide();
+ }
+
+ function destroy() {
+ wss.unbindHandlers(handlerMap);
+ detailsPanel.destroy();
+ }
+
+ angular.module('ovTopo2')
+ .factory('Topo2DeviceDetailsPanel',
+ ['Topo2PanelService', 'GlyphService', 'WebSocketService', 'FlashService',
+ 'ButtonService', 'FnService', 'NavService',
+ function (_ps_, _gs_, _wss_, _flash_, _bs_, _fs_, _ns_) {
+
+ Panel = _ps_;
+ gs = _gs_;
+ wss = _wss_;
+ flash = _flash_;
+ bs = _bs_;
+ fs = _fs_;
+ ns = _ns_;
+
+ return {
+ init: init,
+ updateDetails: updateDetails,
+
+ toggle: toggle,
+ show: show,
+ hide: hide,
+ destroy: destroy
+ };
+ }
+ ]);
+})();