Topo2: ONOS-5640, ONOS-5641 ONOS-5645 Show details for Hosts, Links, Sub-Regions
Added Links panel
Details panel shared between Details, Link, Hosts and Regions
Refactored List content for panel views
Reference to the PanelService Element had a name change
Added a Base UIView to extend future views from
Extend method was being repeated
Change-Id: I3fa070fc5140e98720e47f4b90e3571cb0347596
diff --git a/web/gui/src/main/webapp/app/fw/util/fn.js b/web/gui/src/main/webapp/app/fw/util/fn.js
index 33ad2f6..0d5d4cc 100644
--- a/web/gui/src/main/webapp/app/fw/util/fn.js
+++ b/web/gui/src/main/webapp/app/fw/util/fn.js
@@ -413,6 +413,29 @@
return classes.join(' ');
}
+ function extend(protoProps, staticProps) {
+
+ var parent = this,
+ child;
+
+ child = function () {
+ return parent.apply(this, arguments);
+ };
+
+ angular.extend(child, parent, staticProps);
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function and add the prototype properties.
+ child.prototype = angular.extend({}, parent.prototype, protoProps);
+ child.prototype.constructor = child;
+
+ // Set a convenience property in case the parent's prototype is needed
+ // later.
+ child.__super__ = parent.prototype;
+
+ return child;
+ }
+
angular.module('onosUtil')
.factory('FnService',
@@ -452,7 +475,8 @@
addToTrie: addToTrie,
removeFromTrie: removeFromTrie,
trieLookup: trieLookup,
- classNames: classNames
+ classNames: classNames,
+ extend: extend
};
}]);
diff --git a/web/gui/src/main/webapp/app/fw/widget/listBuilder.js b/web/gui/src/main/webapp/app/fw/widget/listBuilder.js
new file mode 100644
index 0000000..113cb00
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/widget/listBuilder.js
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015-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 -- Widget -- List Service
+ */
+
+(function () {
+ 'use strict';
+
+ function addProp(el, label, value) {
+ var tr = el.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(el) {
+ el.append('tr').append('td').attr('colspan', 2).append('hr');
+ }
+
+ function listProps(el, data) {
+ data.propOrder.forEach(function (p) {
+ if (p === '-') {
+ addSep(el);
+ } else {
+ addProp(el, p, data.props[p]);
+ }
+ });
+ }
+
+ angular.module('onosWidget')
+ .factory('ListService', [
+ function () {
+ return listProps;
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
index 540fe5b..4a7a44a 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
@@ -181,7 +181,7 @@
}
-#ov-topo2 svg .node.device.selected .node-container {
+#ov-topo2 svg .node.selected .node-container {
stroke-width: 2.0;
stroke: #009fdb;
}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js b/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
index 11953e1..ab684eb 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
@@ -22,7 +22,8 @@
(function () {
'use strict';
- var Model;
+ var Model,
+ extend;
function Collection(models, options) {
@@ -91,34 +92,12 @@
}
};
- Collection.extend = function (protoProps, staticProps) {
-
- var parent = this;
- var child;
-
- child = function () {
- return parent.apply(this, arguments);
- };
-
- angular.extend(child, parent, staticProps);
-
- // Set the prototype chain to inherit from `parent`, without calling
- // `parent`'s constructor function and add the prototype properties.
- child.prototype = angular.extend({}, parent.prototype, protoProps);
- child.prototype.constructor = child;
-
- // Set a convenience property in case the parent's prototype is needed
- // later.
- child.__super__ = parent.prototype;
-
- return child;
- };
-
angular.module('ovTopo2')
.factory('Topo2Collection',
- ['Topo2Model',
- function (_Model_) {
+ ['Topo2Model', 'FnService',
+ function (_Model_, fn) {
+ Collection.extend = fn.extend;
Model = _Model_;
return Collection;
}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2DetailsPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2DetailsPanel.js
new file mode 100644
index 0000000..3bea0bc
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2DetailsPanel.js
@@ -0,0 +1,63 @@
+/*
+ * 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;
+
+ // Internal State
+ var detailsPanel;
+
+ // configuration
+ var id = 'topo2-p-detail',
+ className = 'topo-p',
+ panelOpts = {
+ width: 260 // summary and detail panel width
+ };
+
+ function getInstance() {
+ if (detailsPanel) {
+ return detailsPanel;
+ }
+
+ var options = angular.extend({}, panelOpts, {
+ class: className
+ });
+
+ detailsPanel = new Panel(id, options);
+ detailsPanel.el.classed(className, true);
+
+ return detailsPanel;
+ }
+
+ angular.module('ovTopo2')
+ .factory('Topo2DetailsPanelService',
+ ['Topo2PanelService',
+ function (_ps_) {
+
+ Panel = _ps_;
+
+ return getInstance;
+ }
+ ]);
+})();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Device.js b/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
index 19d5554..9b82c90 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Device.js
@@ -74,25 +74,7 @@
},
onClick: function () {
- var ev = d3.event;
-
- if (ev.shiftKey) {
- // TODO: Multi-Select Details Panel
- this.set('selected', true);
- } else {
-
- var s = Boolean(this.get('selected'));
- // Clear all selected Items
- _.each(this.collection.models, function (m) {
- m.set('selected', false);
- });
-
- this.set('selected', !s);
- }
-
- var selected = this.collection.filter(function (m) {
- return m.get('selected');
- });
+ var selected = this.select(d3.event);
if (_.isArray(selected) && selected.length > 0) {
if (selected.length === 1) {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2DeviceDetailsPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2DeviceDetailsPanel.js
index 624b234..741f942 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2DeviceDetailsPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2DeviceDetailsPanel.js
@@ -23,17 +23,13 @@
'use strict';
// Injected Services
- var Panel, gs, wss, flash, bs, fs, ns;
+ var Panel, gs, wss, flash, bs, fs, ns, listProps;
// Internal State
var detailsPanel;
// configuration
var id = 'topo2-p-detail',
- className = 'topo-p',
- panelOpts = {
- width: 260 // summary and detail panel width
- },
handlerMap = {
'showDetails': showDetails
};
@@ -69,43 +65,7 @@
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]);
- }
- });
+ detailsPanel = Panel();
}
function addBtnFooter() {
@@ -136,10 +96,6 @@
cb: function () { ns.navTo(path, { devId: devId }); }
});
}
- // TODO: Implement Overlay service
- // else if (btn = _getButtonDef(id, data)) {
- // addAction(btn);
- // }
});
}
@@ -196,17 +152,17 @@
}
function toggle() {
- var on = detailsPanel.p.toggle(),
+ var on = detailsPanel.el.toggle(),
verb = on ? 'Show' : 'Hide';
flash.flash(verb + ' Details Panel');
}
function show() {
- detailsPanel.p.show();
+ detailsPanel.el.show();
}
function hide() {
- detailsPanel.p.hide();
+ detailsPanel.el.hide();
}
function destroy() {
@@ -216,9 +172,9 @@
angular.module('ovTopo2')
.factory('Topo2DeviceDetailsPanel',
- ['Topo2PanelService', 'GlyphService', 'WebSocketService', 'FlashService',
- 'ButtonService', 'FnService', 'NavService',
- function (_ps_, _gs_, _wss_, _flash_, _bs_, _fs_, _ns_) {
+ ['Topo2DetailsPanelService', 'GlyphService', 'WebSocketService', 'FlashService',
+ 'ButtonService', 'FnService', 'NavService', 'ListService',
+ function (_ps_, _gs_, _wss_, _flash_, _bs_, _fs_, _ns_, _listService_) {
Panel = _ps_;
gs = _gs_;
@@ -227,6 +183,7 @@
bs = _bs_;
fs = _fs_;
ns = _ns_;
+ listProps = _listService_;
return {
init: init,
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Host.js b/web/gui/src/main/webapp/app/view/topo2/topo2Host.js
index 3fdbeb5..2fa7beb 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Host.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Host.js
@@ -48,12 +48,34 @@
angular.module('ovTopo2')
.factory('Topo2HostService', [
'Topo2Collection', 'Topo2NodeModel', 'Topo2ViewService',
- 'IconService', 'Topo2ZoomService',
- function (_Collection_, _NodeModel_, _t2vs_, is, zs) {
+ 'IconService', 'Topo2ZoomService', 'Topo2HostsPanelService',
+ function (_Collection_, _NodeModel_, _t2vs_, is, zs, t2hds) {
Collection = _Collection_;
Model = _NodeModel_.extend({
+ initialize: function () {
+ this.super = this.constructor.__super__;
+ this.super.initialize.apply(this, arguments);
+ },
+ events: {
+ 'click': 'onClick'
+ },
+ onChange: function () {
+ // Update class names when the model changes
+ if (this.el) {
+ this.el.attr('class', this.svgClassName());
+ }
+ },
+ onClick: function () {
+ var selected = this.select(d3.select);
+
+ if (selected.length > 0) {
+ t2hds.displayPanel(this);
+ } else {
+ t2hds.hide();
+ }
+ },
nodeType: 'host',
icon: function () {
var type = this.get('type');
@@ -117,6 +139,7 @@
.attr('text-anchor', 'middle');
this.setScale();
+ this.setUpEvents();
}
});
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2HostsPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2HostsPanel.js
new file mode 100644
index 0000000..b131729
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2HostsPanel.js
@@ -0,0 +1,114 @@
+/*
+ * 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 Layout Module.
+ Module that contains the d3.force.layout logic
+ */
+
+(function () {
+ 'use strict';
+
+ // Injected Services
+ var Panel, gs, wss, flash, listProps;
+
+ // Internal State
+ var hostPanel, hostData;
+
+ function init() {
+ hostPanel = Panel();
+ }
+
+ function formatHostData(data) {
+ return {
+ title: data.get('id'),
+ propOrder: ['MAC', 'IP', 'VLAN', '-', 'Latitude', 'Longitude'],
+ props: {
+ '-': '',
+ 'MAC': data.get('id'),
+ 'IP': data.get('ips')[0],
+ 'VLAN': 'None', // TODO
+ 'Latitude': data.get('location').lat,
+ 'Longitude': data.get('location').lng,
+ }
+ }
+ };
+
+ function displayPanel(data) {
+ init();
+
+ hostData = formatHostData(data);
+ render();
+ }
+
+ function render() {
+ hostPanel.el.show();
+ hostPanel.emptyRegions();
+
+ var svg = hostPanel.appendToHeader('div')
+ .classed('icon', true)
+ .append('svg'),
+ title = hostPanel.appendToHeader('h2'),
+ table = hostPanel.appendToBody('table'),
+ tbody = table.append('tbody');
+
+ title.text(hostData.title);
+ gs.addGlyph(svg, 'bird', 24, 0, [1, 1]);
+ listProps(tbody, hostData);
+ }
+
+ function show() {
+ hostPanel.el.show();
+ }
+
+ function hide() {
+ hostPanel.el.hide();
+ }
+
+ function toggle() {
+ var on = hostPanel.el.toggle(),
+ verb = on ? 'Show' : 'Hide';
+ flash.flash(verb + ' host Panel');
+ }
+
+ function destroy() {
+ hostPanel.destroy();
+ }
+
+ angular.module('ovTopo2')
+ .factory('Topo2HostsPanelService',
+ ['Topo2DetailsPanelService', 'GlyphService', 'WebSocketService', 'FlashService', 'ListService',
+ function (_ps_, _gs_, _wss_, _flash_, _listService_) {
+
+ Panel = _ps_;
+ gs = _gs_;
+ wss = _wss_;
+ flash = _flash_;
+ listProps = _listService_;
+
+ return {
+ displayPanel: displayPanel,
+ init: init,
+ show: show,
+ hide: hide,
+ toggle: toggle,
+ destroy: destroy,
+ isVisible: function () { return hostPanel.isVisible(); }
+ };
+ }
+ ]);
+
+})();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
index 419f688..b552a9e 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
@@ -22,7 +22,7 @@
(function () {
'use strict';
- var $log, Collection, Model, ts, sus, t2zs, t2vs;
+ var $log, Collection, Model, ts, sus, t2zs, t2vs, t2lps, fn;
var linkLabelOffset = '0.35em';
@@ -124,6 +124,16 @@
type: function () {
return this.get('type');
},
+ svgClassName: function () {
+ return fn.classNames('link',
+ this.nodeType,
+ this.get('type'),
+ {
+ enhanced: this.get('enhanced'),
+ selected: this.get('selected')
+ }
+ );
+ },
expected: function () {
// TODO: original code is: (s && s.expected) && (t && t.expected);
return true;
@@ -137,6 +147,12 @@
return (sourceOnline) && (targetOnline);
},
+ onChange: function () {
+ // Update class names when the model changes
+ if (this.el) {
+ this.el.attr('class', this.svgClassName());
+ }
+ },
enhance: function () {
var data = [],
point;
@@ -145,7 +161,7 @@
link.unenhance();
});
- this.el.classed('enhanced', true);
+ this.set('enhanced', true);
if (showPort()) {
point = this.locatePortLabel();
@@ -193,9 +209,36 @@
}
},
unenhance: function () {
- this.el.classed('enhanced', false);
+ this.set('enhanced', false);
d3.select('#topo-portLabels').selectAll('.portLabel').remove();
},
+ select: function () {
+ var ev = d3.event;
+
+ // TODO: if single selection clear selected devices, hosts, sub-regions
+ var s = Boolean(this.get('selected'));
+ // Clear all selected Items
+ _.each(this.collection.models, function (m) {
+ m.set('selected', false);
+ });
+
+ this.set('selected', !s);
+
+ var selected = this.collection.filter(function (m) {
+ return m.get('selected');
+ });
+
+ return selected;
+ },
+ showDetails: function () {
+ var selected = this.select(d3.event);
+
+ if (selected) {
+ t2lps.displayLink(this);
+ } else {
+ t2lps.hide();
+ }
+ },
locatePortLabel: function (src) {
var offset = 32 / (labelDim * t2zs.scale()),
@@ -259,6 +302,7 @@
// from mouse position.
this.el.on('mouseover', this.enhance.bind(this));
this.el.on('mouseout', this.unenhance.bind(this));
+ this.el.on('click', this.showDetails.bind(this));
if (this.get('type') === 'hostLink') {
// sus.visible(link, api.showHosts());
@@ -277,7 +321,7 @@
},
update: function () {
- if (this.el.classed('enhanced')) {
+ if (this.get('enhanced')) {
this.enhance();
}
}
@@ -298,9 +342,9 @@
.factory('Topo2LinkService',
['$log', 'Topo2Collection', 'Topo2Model',
'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
- 'Topo2ViewService',
+ 'Topo2ViewService', 'Topo2LinkPanelService', 'FnService',
function (_$log_, _Collection_, _Model_, _ts_, _sus_,
- _t2zs_, _t2vs_) {
+ _t2zs_, _t2vs_, _t2lps_, _fn_) {
$log = _$log_;
ts = _ts_;
@@ -309,6 +353,8 @@
t2vs = _t2vs_;
Collection = _Collection_;
Model = _Model_;
+ t2lps = _t2lps_;
+ fn = _fn_;
return {
createLinkCollection: createLinkCollection
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2LinkPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2LinkPanel.js
new file mode 100644
index 0000000..cb65ced
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2LinkPanel.js
@@ -0,0 +1,127 @@
+/*
+ * 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 Layout Module.
+ Module that contains the d3.force.layout logic
+ */
+
+(function () {
+ 'use strict';
+
+ // Injected Services
+ var Panel, gs, wss, flash, listProps;
+
+ // Internal State
+ var linkPanel, linkData;
+
+ function init() {
+ linkPanel = Panel();
+ }
+
+ function formatLinkData(data) {
+
+ var source = data.get('source'),
+ target = data.get('target');
+
+ return {
+ title: 'Link',
+ propOrder: [
+ 'Type', '-',
+ 'A Type', 'A Id', 'A Label', 'A Port', '-',
+ 'B Type', 'B Id', 'B Label', 'B Port'
+ ],
+ props: {
+ '-': '',
+ 'Type': data.get('type'),
+ 'A Type': source.get('nodeType'),
+ 'A Id': source.get('id'),
+ 'A Label': 'Label',
+ 'A Port': data.get('portA') || '',
+ 'B Type': target.get('nodeType'),
+ 'B Id': target.get('id'),
+ 'B Label': 'Label',
+ 'B Port': data.get('portB') || '',
+ }
+ }
+ };
+
+ function displayLink(data) {
+ init();
+
+ linkData = formatLinkData(data);
+ render();
+ }
+
+ function render() {
+ linkPanel.el.show();
+ linkPanel.emptyRegions();
+
+ var svg = linkPanel.appendToHeader('div')
+ .classed('icon', true)
+ .append('svg'),
+ title = linkPanel.appendToHeader('h2'),
+ table = linkPanel.appendToBody('table'),
+ tbody = table.append('tbody');
+
+ title.text(linkData.title);
+ gs.addGlyph(svg, 'bird', 24, 0, [1, 1]);
+ listProps(tbody, linkData);
+ }
+
+ function show() {
+ linkPanel.el.show();
+ }
+
+ function hide() {
+ linkPanel.el.hide();
+ }
+
+ function toggle() {
+ var on = linkPanel.el.toggle(),
+ verb = on ? 'Show' : 'Hide';
+ flash.flash(verb + ' Link Panel');
+ }
+
+ function destroy() {
+ wss.unbindHandlers(handlerMap);
+ linkPanel.destroy();
+ }
+
+ angular.module('ovTopo2')
+ .factory('Topo2LinkPanelService',
+ ['Topo2DetailsPanelService', 'GlyphService', 'WebSocketService', 'FlashService', 'ListService',
+ function (_ps_, _gs_, _wss_, _flash_, _listService_) {
+
+ Panel = _ps_;
+ gs = _gs_;
+ wss = _wss_;
+ flash = _flash_;
+ listProps = _listService_;
+
+ return {
+ displayLink: displayLink,
+ init: init,
+ show: show,
+ hide: hide,
+ toggle: toggle,
+ destroy: destroy,
+ isVisible: function () { return linkPanel.isVisible(); }
+ };
+ }
+ ]);
+
+})();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Model.js b/web/gui/src/main/webapp/app/view/topo2/topo2Model.js
index b1b2a12..f76456e 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Model.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Model.js
@@ -22,6 +22,8 @@
(function () {
'use strict';
+ var extend;
+
function Model(attributes) {
var attrs = attributes || {};
@@ -118,34 +120,13 @@
}
};
- Model.extend = function (protoProps, staticProps) {
-
- var parent = this;
- var child;
-
- child = function () {
- return parent.apply(this, arguments);
- };
-
- angular.extend(child, parent, staticProps);
-
- // Set the prototype chain to inherit from `parent`, without calling
- // `parent`'s constructor function and add the prototype properties.
- child.prototype = angular.extend({}, parent.prototype, protoProps);
- child.prototype.constructor = child;
-
- // Set a convenience property in case the parent's prototype is needed
- // later.
- child.__super__ = parent.prototype;
-
- return child;
- };
-
angular.module('ovTopo2')
.factory('Topo2Model', [
- function () {
+ 'FnService',
+ function (fn) {
+ Model.extend = fn.extend;
+
return Model;
}
]);
-
})();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js b/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
index 048e2f9..18ad7cc 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2NodeModel.js
@@ -76,6 +76,31 @@
'mouseout': 'mouseoutHandler'
};
},
+ select: function () {
+ var ev = d3.event;
+
+ // TODO: if single selection clear selected devices, hosts, sub-regions
+
+ if (ev.shiftKey) {
+ // TODO: Multi-Select Details Panel
+ this.set('selected', true);
+ } else {
+
+ var s = Boolean(this.get('selected'));
+ // Clear all selected Items
+ _.each(this.collection.models, function (m) {
+ m.set('selected', false);
+ });
+
+ this.set('selected', !s);
+ }
+
+ var selected = this.collection.filter(function (m) {
+ return m.get('selected');
+ });
+
+ return selected;
+ },
createNode: function () {
this.set('svgClass', this.svgClassName());
t2nps.positionNode(this);
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Panel.js b/web/gui/src/main/webapp/app/view/topo2/topo2Panel.js
index 3687f6b..686ec96 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Panel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Panel.js
@@ -22,30 +22,24 @@
(function () {
'use strict';
- var ps;
+ // Injected Services
+ var flash, ps;
- var Panel = function (id, options) {
- this.id = id;
- this.p = ps.createPanel(this.id, options);
- this.setup();
+ var panel = {
+ initialize: function (id, options) {
+ this.id = id;
+ this.el = ps.createPanel(id, options);
+ this.setup();
- if (options.show) {
- this.p.show();
- }
- };
-
- Panel.prototype = {
+ if (options.show) {
+ this.el.show();
+ }
+ },
setup: function () {
- var panel = this.p;
- panel.empty();
-
- panel.append('div').classed('header', true);
- panel.append('div').classed('body', true);
- panel.append('div').classed('footer', true);
-
- this.header = panel.el().select('.header');
- this.body = panel.el().select('.body');
- this.footer = panel.el().select('.body');
+ this.el.empty();
+ this.header = this.el.append('div').classed('header', true);
+ this.body = this.el.append('div').classed('body', true);
+ this.footer = this.el.append('div').classed('footer', true);
},
appendToHeader: function (x) {
return this.header.append(x);
@@ -65,15 +59,19 @@
ps.destroyPanel(this.id);
},
isVisible: function () {
- return this.p.isVisible();
+ return this.el.isVisible();
}
- };
+ }
angular.module('ovTopo2')
- .factory('Topo2PanelService', ['PanelService',
- function (_ps_) {
+ .factory('Topo2PanelService',
+ ['Topo2UIView', 'FlashService', 'PanelService',
+ function (View, _flash_, _ps_) {
+
+ flash = _flash_;
ps = _ps_;
- return Panel;
+
+ return View.extend(panel);
}
]);
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
index bd24bbb..24df147 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
@@ -54,8 +54,6 @@
link.createLink();
});
- console.log(region.get('id'));
-
// TEMP Map Zoom
var regionPanZooms = {
"(root)": {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js b/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js
index bb64578..a370746 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2SubRegion.js
@@ -41,9 +41,9 @@
angular.module('ovTopo2')
.factory('Topo2SubRegionService',
['WebSocketService', 'Topo2Collection', 'Topo2NodeModel',
- 'ThemeService', 'Topo2ViewService',
+ 'ThemeService', 'Topo2ViewService', 'Topo2SubRegionPanelService',
- function (_wss_, _c_, _NodeModel_, _ts_, _t2vs_) {
+ function (_wss_, _c_, _NodeModel_, _ts_, _t2vs_m, _t2srp_) {
wss = _wss_;
Collection = _c_;
@@ -54,13 +54,29 @@
this.super.initialize.apply(this, arguments);
},
events: {
- 'dblclick': 'navigateToRegion'
+ 'dblclick': 'navigateToRegion',
+ 'click': 'onClick'
+ },
+ onChange: function () {
+ // Update class names when the model changes
+ if (this.el) {
+ this.el.attr('class', this.svgClassName());
+ }
},
nodeType: 'sub-region',
icon: function () {
var type = this.get('type');
return remappedDeviceTypes[type] || type || 'm_cloud';
},
+ onClick: function () {
+ var selected = this.select(d3.event);
+
+ if (selected.length > 0) {
+ _t2srp_.displayPanel(this);
+ } else {
+ _t2srp_.hide();
+ }
+ },
navigateToRegion: function () {
if (d3.event.defaultPrevented) return;
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2SubRegionPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2SubRegionPanel.js
new file mode 100644
index 0000000..b5e49b7
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2SubRegionPanel.js
@@ -0,0 +1,112 @@
+/*
+ * 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 Layout Module.
+ Module that contains the d3.force.layout logic
+ */
+
+(function () {
+ 'use strict';
+
+ // Injected Services
+ var Panel, gs, wss, flash, listProps;
+
+ // Internal State
+ var subRegionPanel, subRegionData;
+
+ function init() {
+ subRegionPanel = Panel();
+ }
+
+ function formatSubRegionData(data) {
+ return {
+ title: data.get('name'),
+ propOrder: ['Id', 'Type', '-', 'Number of Devices', 'Number of Hosts'],
+ props: {
+ '-': '',
+ 'Id': data.get('id'),
+ 'Type': data.get('nodeType'),
+ 'Number of Devices': data.get('nDevs'),
+ 'Number of Hosts': data.get('nHosts')
+ }
+ }
+ };
+
+ function displayPanel(data) {
+ init();
+ subRegionData = formatSubRegionData(data);
+ render();
+ }
+
+ function render() {
+ subRegionPanel.el.show();
+ subRegionPanel.emptyRegions();
+
+ var svg = subRegionPanel.appendToHeader('div')
+ .classed('icon', true)
+ .append('svg'),
+ title = subRegionPanel.appendToHeader('h2'),
+ table = subRegionPanel.appendToBody('table'),
+ tbody = table.append('tbody');
+
+ title.text(subRegionData.title);
+ gs.addGlyph(svg, 'bird', 24, 0, [1, 1]);
+ listProps(tbody, subRegionData);
+ }
+
+ function show() {
+ subRegionPanel.el.show();
+ }
+
+ function hide() {
+ subRegionPanel.el.hide();
+ }
+
+ function toggle() {
+ var on = subRegionPanel.el.toggle(),
+ verb = on ? 'Show' : 'Hide';
+ flash.flash(verb + ' subRegion Panel');
+ }
+
+ function destroy() {
+ subRegionPanel.destroy();
+ }
+
+ angular.module('ovTopo2')
+ .factory('Topo2SubRegionPanelService',
+ ['Topo2DetailsPanelService', 'GlyphService', 'WebSocketService', 'FlashService', 'ListService',
+ function (_ps_, _gs_, _wss_, _flash_, _listService_) {
+
+ Panel = _ps_;
+ gs = _gs_;
+ wss = _wss_;
+ flash = _flash_;
+ listProps = _listService_;
+
+ return {
+ displayPanel: displayPanel,
+ init: init,
+ show: show,
+ hide: hide,
+ toggle: toggle,
+ destroy: destroy,
+ isVisible: function () { return subRegionPanel.isVisible(); }
+ };
+ }
+ ]);
+
+})();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js
index 43e92fb..b7e671c 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js
@@ -23,7 +23,7 @@
'use strict';
// Injected Services
- var Panel, gs, wss, flash;
+ var Panel, gs, wss, flash, listProps;
// Internal State
var summaryPanel, summaryData;
@@ -33,7 +33,7 @@
className = 'topo-p',
panelOpts = {
show: true,
- width: 260 // summary and detail panel width
+ width: 260 // summary and detail panel width
},
handlerMap = {
showSummary: handleSummaryData
@@ -49,37 +49,7 @@
});
summaryPanel = new Panel(id, options);
- summaryPanel.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) {
- summaryData.propOrder.forEach(function (p) {
- if (p === '-') {
- addSep(tbody);
- } else {
- addProp(tbody, p, summaryData.props[p]);
- }
- });
+ summaryPanel.el.classed(className, true);
}
function render() {
@@ -94,7 +64,7 @@
title.text(summaryData.title);
gs.addGlyph(svg, 'bird', 24, 0, [1, 1]);
- listProps(tbody);
+ listProps(tbody, summaryData);
}
function handleSummaryData(data) {
@@ -107,7 +77,7 @@
}
function toggle() {
- var on = summaryPanel.p.toggle(),
+ var on = summaryPanel.el.toggle(),
verb = on ? 'Show' : 'Hide';
flash.flash(verb + ' Summary Panel');
}
@@ -119,17 +89,17 @@
angular.module('ovTopo2')
.factory('Topo2SummaryPanelService',
- ['Topo2PanelService', 'GlyphService', 'WebSocketService', 'FlashService',
- function (_ps_, _gs_, _wss_, _flash_) {
+ ['Topo2PanelService', 'GlyphService', 'WebSocketService', 'FlashService', 'ListService',
+ function (_ps_, _gs_, _wss_, _flash_, _listService_) {
Panel = _ps_;
gs = _gs_;
wss = _wss_;
flash = _flash_;
+ listProps = _listService_;
return {
init: init,
-
toggle: toggle,
destroy: destroy,
isVisible: function () { return summaryPanel.isVisible(); }
diff --git a/web/gui/src/main/webapp/app/view/topo2/uiView.js b/web/gui/src/main/webapp/app/view/topo2/uiView.js
new file mode 100644
index 0000000..8bc01cc
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/uiView.js
@@ -0,0 +1,58 @@
+/*
+ * 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 -- Base UIView class.
+ A base class for UIViews to extend from
+ */
+
+(function () {
+ 'use strict';
+
+ function View(options) {
+ if (options && options.el) {
+ this.el = options.el;
+ this.$el = angular.element(this.el);
+ }
+
+ this.initialize.apply(this, arguments);
+ }
+
+ angular.module('ovTopo2')
+ .factory('Topo2UIView',
+ ['FnService',
+ function (fn) {
+
+ _.extend(View.prototype, {
+ el: null,
+ empty: function () {
+ if (this.$el) {
+ this.$el.empty();
+ }
+ },
+ destroy: function () {
+ // TODO: Unbind Events
+ this.empty();
+ return this;
+ }
+ });
+
+ View.extend = fn.extend;
+ return View;
+ }
+ ]);
+
+})();
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 0f91449..9f321f2 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -83,6 +83,7 @@
<script src="app/fw/widget/button.js"></script>
<script src="app/fw/widget/tableBuilder.js"></script>
<script src="app/fw/widget/chartBuilder.js"></script>
+ <script src="app/fw/widget/listBuilder.js"></script>
<script src="app/fw/layer/layer.js"></script>
<script src="app/fw/layer/panel.js"></script>
@@ -127,20 +128,23 @@
<link rel="stylesheet" href="app/fw/widget/table-theme.css">
<!-- Under development for Region support. -->
- <!-- <script src="app/view/topo2/topo2.js"></script>
+ <!--<script src="app/view/topo2/topo2.js"></script>
<script src="app/view/topo2/topo2Breadcrumb.js"></script>
<script src="app/view/topo2/topo2Collection.js"></script>
<script src="app/view/topo2/topo2D3.js"></script>
<script src="app/view/topo2/topo2Dialog.js"></script>
+ <script src="app/view/topo2/topo2DetailsPanel.js"></script>
<script src="app/view/topo2/topo2Device.js"></script>
<script src="app/view/topo2/topo2DeviceDetailsPanel.js"></script>
<script src="app/view/topo2/topo2Event.js"></script>
<script src="app/view/topo2/topo2Force.js"></script>
<script src="app/view/topo2/topo2Host.js"></script>
+ <script src="app/view/topo2/topo2HostsPanel.js"></script>
<script src="app/view/topo2/topo2Instance.js"></script>
<script src="app/view/topo2/topo2KeyCommands.js"></script>
<script src="app/view/topo2/topo2Layout.js"></script>
<script src="app/view/topo2/topo2Link.js"></script>
+ <script src="app/view/topo2/topo2LinkPanel.js"></script>
<script src="app/view/topo2/topo2Map.js"></script>
<script src="app/view/topo2/topo2MapCountryFilters.js"></script>
<script src="app/view/topo2/topo2MapConfig.js"></script>
@@ -154,9 +158,11 @@
<script src="app/view/topo2/topo2Select.js"></script>
<script src="app/view/topo2/topo2SummaryPanel.js"></script>
<script src="app/view/topo2/topo2SubRegion.js"></script>
+ <script src="app/view/topo2/topo2SubRegionPanel.js"></script>
<script src="app/view/topo2/topo2Theme.js"></script>
<script src="app/view/topo2/topo2View.js"></script>
<script src="app/view/topo2/topo2Zoom.js"></script>
+ <script src="app/view/topo2/uiView.js"></script>
<link rel="stylesheet" href="app/view/topo2/topo2.css">
<link rel="stylesheet" href="app/view/topo2/topo2-theme.css">-->
diff --git a/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js b/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
index e535460..bc434ca 100644
--- a/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
@@ -212,12 +212,12 @@
it('should define api functions', function () {
expect(fs.areFunctions(fs, [
'isF', 'isA', 'isS', 'isO', 'contains',
- 'areFunctions', 'areFunctionsNonStrict', 'windowSize',
+ 'areFunctions', 'areFunctionsNonStrict', 'windowSize',
'isMobile', 'isChrome', 'isSafari', 'isFirefox',
'debugOn', 'debug',
'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
'eecode', 'noPx', 'noPxStyle', 'endsWith', 'parseBitRate', 'addToTrie', 'removeFromTrie', 'trieLookup',
- 'classNames'
+ 'classNames', 'extend'
])).toBeTruthy();
});