Steven Burrows

CSS Added fill-mode for nodes

Amends based on Simons Comments
ESLinted The whole project
Topo2Link - Added Labels for PortA and PortB on mouseover
Updated breadcrumbs
Topo2.js - commented out a line causing error on panning
Topo2 Navigation between regions

Change-Id: I9cc0f4499ab68a14e246bba192f6528258471b35
module.exports = {
"extends": "google",
"installedESLint": true,
"globals": {
"angular": true,
"d3": true
},
"rules": {
"brace-style": 0,
"no-void": 0,
"require-jsdoc": 0,
"padded-blocks": 0,
"quote-props": 0,
"no-warning-comments": 0,
"object-curly-spacing": ["error", "always"],
"indent": ["error", 4],
"one-var": 0,
"space-before-function-paren": ["error", { "anonymous": "always", "named": "never" }]
}
};
{
"name": "topo2",
"version": "1.0.0",
"description": "ONOS Topo2",
"main": "topo2.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "./node_modules/.bin/eslint ./**/*.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"eslint": "^3.4.0",
"eslint-config-google": "^0.6.0"
}
}
......@@ -66,3 +66,10 @@
.floatpanel {
top: 104px;
}
/* -- Base Device Styles -- */
#ov-topo2 svg .node {
cursor: pointer;
fill-rule: evenodd;
}
\ No newline at end of file
......
......@@ -24,18 +24,14 @@
'use strict';
// references to injected services
var $scope, $log, $loc,
fs, mast, ks, zs,
gs, ms, sus, flash,
wss, ps, th,
t2es, t2fs, t2is, t2bcs;
var $scope, $log, fs, mast, ks, zs,
gs, sus, ps, t2es, t2fs, t2is, t2bcs;
// DOM elements
var ovtopo2, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer;
var ovtopo2, svg, defs, zoomLayer, forceG;
// Internal state
var zoomer, actionMap;
var zoomer;
// --- Glyphs, Icons, and the like -----------------------------------
......@@ -66,10 +62,10 @@
var sc = zoomer.scale(),
tr = zoomer.translate();
ps.setPrefs('topo_zoom', {tx:tr[0], ty:tr[1], sc:sc});
ps.setPrefs('topo_zoom', { tx: tr[0], ty: tr[1], sc: sc });
// keep the map lines constant width while zooming
mapG.style('stroke-width', (2.0 / sc) + 'px');
// mapG.style('stroke-width', (2.0 / sc) + 'px');
}
function setUpZoom() {
......@@ -82,7 +78,6 @@
});
}
// === Controller Definition -----------------------------------------
angular.module('ovTopo2', ['onosUtil', 'onosSvg', 'onosRemote'])
......@@ -101,7 +96,6 @@
_t2es_, _t2fs_, _t2is_, _t2bcs_) {
var params = _$loc_.search(),
projection,
dim,
wh,
uplink = {
......@@ -109,14 +103,13 @@
// showNoDevs: showNoDevs,
// projection: function () { return projection; },
zoomLayer: function () { return zoomLayer; },
zoomer: function () { return zoomer; },
zoomer: function () { return zoomer; }
// opacifyMap: opacifyMap,
// topoStartDone: topoStartDone
};
$scope = _$scope_;
$log = _$log_;
$loc = _$loc_;
fs = _fs_;
mast = _mast_;
......@@ -124,13 +117,9 @@
zs = _zs_;
gs = _gs_;
ms = _ms_;
sus = _sus_;
flash = _flash_;
wss = _wss_;
ps = _ps_;
th = _th_;
t2es = _t2es_;
t2fs = _t2fs_;
......@@ -140,7 +129,10 @@
// capture selected intent parameters (if they are set in the
// query string) so that the traffic overlay can highlight
// the path for that intent
if (params.intentKey && params.intentAppId && params.intentAppName) {
if (params.intentKey &&
params.intentAppId &&
params.intentAppName) {
$scope.intentData = {
key: params.intentKey,
appId: params.intentAppId,
......@@ -169,7 +161,6 @@
svg.attr(wh);
dim = [wh.width, wh.height];
// set up our keyboard shortcut bindings
setUpKeys();
setUpZoom();
......@@ -183,7 +174,6 @@
t2fs.init(svg, forceG, uplink, dim);
t2bcs.init();
// =-=-=-=-=-=-=-=-
// TODO: in future, we will load background map data
// asynchronously (hence the promise) and then chain off
......@@ -191,12 +181,8 @@
// For now, we'll send the event inline...
t2es.start();
t2is.initInst({ showMastership: t2fs.showMastership });
// === ORIGINAL CODE ===
// setUpKeys();
......@@ -242,4 +228,4 @@
$log.log('OvTopo2Ctrl has been created');
}]);
}());
})();
......
......@@ -19,16 +19,18 @@
Module that renders the breadcrumbs for regions
*/
(function () {
(function () {
'use strict';
var $log, wss;
// Internal
var breadcrumbContainer,
breadcrumbs;
function init() {
$log.debug("Topo2BreadcrumbService Initiated");
breadcrumbs = [];
breadcrumbContainer = d3.select('#breadcrumbs');
render();
......@@ -36,13 +38,8 @@
function addBreadcrumb(crumbs) {
// If `crumbs` is an array, merge with breadcrumbs;
if (crumbs.length) {
breadcrumbs = breadcrumbs.concat(crumbs);
} else {
breadcrumbs.push(crumbs);
}
breadcrumbContainer.selectAll('.breadcrumb').remove();
breadcrumbs = crumbs.reverse();
render();
}
......
......@@ -26,12 +26,14 @@
function Collection(models, options) {
options || (options = {});
var opts = options || (options = {});
this.models = [];
this._reset();
if (options.comparator !== void 0) this.comparator = options.comparator;
if (opts.comparator) {
this.comparator = opts.comparator;
}
if (models) {
this.add(models);
......@@ -48,7 +50,8 @@
data.forEach(function (d) {
var model = new _this.model(d);
var CollectionModel = _this.model;
var model = new CollectionModel(d);
model.collection = _this;
_this.models.push(model);
......@@ -57,9 +60,11 @@
}
},
get: function (id) {
if (!id) {
return void 0;
return null;
}
return this._byId[id] || null;
},
sort: function () {
......@@ -76,9 +81,11 @@
this._byId = [];
this.models = [];
},
toJSON: function(options) {
return this.models.map(function(model) { return model.toJSON(options); });
},
toJSON: function (options) {
return this.models.map(function (model) {
return model.toJSON(options);
});
}
};
Collection.extend = function (protoProps, staticProps) {
......
......@@ -22,99 +22,21 @@ Module that contains the d3.force.layout logic
(function () {
'use strict';
var sus, is, ts;
// internal state
var deviceLabelIndex = 0,
hostLabelIndex = 0;
// configuration
var devIconDim = 36,
labelPad = 4,
hostRadius = 14,
badgeConfig = {
radius: 12,
yoff: 5,
gdelta: 10
},
halfDevIcon = devIconDim / 2,
devBadgeOff = { dx: -halfDevIcon, dy: -halfDevIcon },
hostBadgeOff = { dx: -hostRadius, dy: -hostRadius },
status = {
i: 'badgeInfo',
w: 'badgeWarn',
e: 'badgeError'
};
// note: these are the device icon colors without affinity (no master)
var dColTheme = {
light: {
online: '#444444',
offline: '#cccccc'
},
dark: {
// TODO: theme
online: '#444444',
offline: '#cccccc'
}
};
function init() {}
function renderBadge(node, bdg, boff) {
var bsel,
bcr = badgeConfig.radius,
bcgd = badgeConfig.gdelta;
node.select('g.badge').remove();
bsel = node.append('g')
.classed('badge', true)
.classed(badgeStatus(bdg), true)
.attr('transform', sus.translate(boff.dx, boff.dy));
bsel.append('circle')
.attr('r', bcr);
if (bdg.txt) {
bsel.append('text')
.attr('dy', badgeConfig.yoff)
.attr('text-anchor', 'middle')
.text(bdg.txt);
} else if (bdg.gid) {
bsel.append('use')
.attr({
width: bcgd * 2,
height: bcgd * 2,
transform: sus.translate(-bcgd, -bcgd),
'xlink:href': '#' + bdg.gid
});
}
}
// TODO: Move to Device Model when working on the Exit Devices
function updateDeviceRendering(d) {
var node = d.el,
bdg = d.badge,
label = trimLabel(deviceLabel(d)),
labelWidth;
var is;
node.select('text').text(label);
labelWidth = label ? computeLabelWidth(node) : 0;
// Configuration
var hostRadius = 14;
node.select('rect')
.transition()
.attr(iconBox(devIconDim, labelWidth));
if (bdg) {
renderBadge(node, bdg, devBadgeOff);
}
}
function init() {}
function nodeEnter(node) {
node.onEnter(this, node);
}
function nodeExit(node) {
node.onExit(this, node);
}
function hostLabel(d) {
return d.get('id');
......@@ -144,19 +66,18 @@ Module that contains the d3.force.layout logic
angular.module('ovTopo2')
.factory('Topo2D3Service',
['SvgUtilService', 'IconService', 'ThemeService',
['IconService',
function (_sus_, _is_, _ts_) {
sus = _sus_;
function (_is_) {
is = _is_;
ts = _ts_;
return {
init: init,
nodeEnter: nodeEnter,
nodeExit: nodeExit,
hostEnter: hostEnter,
linkEntering: linkEntering
}
};
}
]
);
......
......@@ -22,7 +22,7 @@
(function () {
'use strict';
var Collection, Model, is, sus, ts, t2vs;
var Collection, Model, is, sus, ts;
var remappedDeviceTypes = {
virtual: 'cord'
......@@ -30,30 +30,17 @@
// configuration
var devIconDim = 36,
labelPad = 10,
hostRadius = 14,
badgeConfig = {
radius: 12,
yoff: 5,
gdelta: 10
},
halfDevIcon = devIconDim / 2,
devBadgeOff = { dx: -halfDevIcon, dy: -halfDevIcon },
hostBadgeOff = { dx: -hostRadius, dy: -hostRadius },
status = {
i: 'badgeInfo',
w: 'badgeWarn',
e: 'badgeError'
},
deviceLabelIndex = 0;
halfDevIcon = devIconDim / 2;
function createDeviceCollection(data, region) {
var DeviceCollection = Collection.extend({
model: Model,
comparator: function(a, b) {
var order = region.get('layerOrder');
return order.indexOf(a.get('layer')) - order.indexOf(b.get('layer'));
comparator: function (a, b) {
var order = region.get('layerOrder'),
aLayer = a.get('layer'),
bLayer = b.get('layer');
return order.indexOf(aLayer - order.indexOf(bLayer));
}
});
......@@ -80,7 +67,7 @@
y: -dim / 2,
width: dim + labelWidth,
height: dim
}
};
}
// note: these are the device icon colors without affinity (no master)
......@@ -100,8 +87,9 @@
var o = this.node.online,
id = this.node.master, // TODO: This should be from node.master
otag = o ? 'online' : 'offline';
return o ? sus.cat7().getColor(id, 0, ts.theme())
: dColTheme[ts.theme()][otag];
return o ? sus.cat7().getColor(id, 0, ts.theme()) :
dColTheme[ts.theme()][otag];
}
function setDeviceColor() {
......@@ -111,18 +99,17 @@
angular.module('ovTopo2')
.factory('Topo2DeviceService',
['Topo2Collection', 'Topo2NodeModel', 'IconService', 'SvgUtilService',
'ThemeService', 'Topo2ViewService',
['Topo2Collection', 'Topo2NodeModel', 'IconService',
'SvgUtilService', 'ThemeService',
function (_Collection_, _NodeModel_, _is_, _sus_, _ts_, classnames, _t2vs_) {
function (_c_, _nm_, _is_, _sus_, _ts_, classnames) {
t2vs = _t2vs_;
is = _is_;
sus = _sus_;
ts = _ts_;
Collection = _Collection_;
Collection = _c_;
Model = _NodeModel_.extend({
Model = _nm_.extend({
initialize: function () {
this.set('weight', 0);
this.constructor.__super__.initialize.apply(this, arguments);
......@@ -152,7 +139,19 @@
node.attr('transform', sus.translate(-halfDevIcon, -halfDevIcon));
this.render();
},
onExit: function () {},
onExit: function () {
var node = this.el;
node.select('use')
.style('opacity', 0.5)
.transition()
.duration(800)
.style('opacity', 0);
node.selectAll('rect')
.style('stroke-fill', '#555')
.style('fill', '#888')
.style('opacity', 0.5);
},
render: function () {
this.setDeviceColor();
}
......
......@@ -34,7 +34,6 @@
openListener;
// TODO: only add heartbeat timer etc. if we really need to be doing that..
// ========================== Helper Functions
function createHandlerMap() {
......@@ -100,4 +99,4 @@
stop: stop
};
}]);
}());
})();
......
......@@ -26,43 +26,12 @@
var $log,
wss;
// SVG elements;
var linkG,
linkLabelG,
numLinkLblsG,
portLabelG,
nodeG;
// internal state
var settings, // merged default settings and options
force, // force layout object
drag, // drag behavior handler
network = {
nodes: [],
links: [],
linksByDevice: {},
lookup: {},
revLinkToKey: {}
},
lu, // shorthand for lookup
rlk, // shorthand for revLinktoKey
showHosts = false, // whether hosts are displayed
showOffline = true, // whether offline devices are displayed
nodeLock = false, // whether nodes can be dragged or not (locked)
fTimer, // timer for delayed force layout
fNodesTimer, // timer for delayed nodes update
fLinksTimer, // timer for delayed links update
dim, // the dimensions of the force layout [w,h]
linkNums = []; // array of link number labels
// D3 selections;
var link,
linkLabel,
node;
var $log, wss, t2is, t2rs, t2ls, t2vs, t2bcs;
var t2is, t2rs, t2ls, t2vs, t2bcs;
var svg, forceG, uplink, dim, opts;
// D3 Selections
var node;
// ========================== Helper Functions
function init(_svg_, _forceG_, _uplink_, _dim_, _opts_) {
......@@ -70,7 +39,9 @@
forceG = _forceG_;
uplink = _uplink_;
dim = _dim_;
opts = _opts_
opts = _opts_;
t2ls.init(svg, forceG, uplink, dim, opts);
}
function destroy() {
......@@ -91,7 +62,7 @@
var parentRegion = data.parent;
var span = topdiv.select('.parentRegion').select('span');
span.text(parentRegion || '[no parent]');
span.classed('nav-me', !!parentRegion);
span.classed('nav-me', Boolean(parentRegion));
}
function doTmpCurrentRegion(data) {
......@@ -162,30 +133,23 @@
$log.debug('>> topo2CurrentRegion event:', data);
doTmpCurrentRegion(data);
t2rs.addRegion(data);
t2ls.init(svg, forceG, uplink, dim, opts);
t2ls.update();
t2ls.start();
t2ls.createForceLayout();
}
function topo2PeerRegions(data) {
$log.debug('>> topo2PeerRegions event:', data)
$log.debug('>> topo2PeerRegions event:', data);
doTmpPeerRegions(data);
}
function topo2PeerRegions(data) {
$log.debug('>> topo2PeerRegions event:', data)
}
function startDone(data) {
$log.debug('>> topo2StartDone event:', data);
}
function showMastership(masterId) {
if (!masterId) {
restoreLayerState();
} else {
if (masterId) {
showMastershipFor(masterId);
} else {
restoreLayerState();
}
}
......@@ -224,10 +188,6 @@
t2ls.setDimensions();
}
function getDim() {
return dim;
}
// ========================== Main Service Definition
angular.module('ovTopo2')
......@@ -259,4 +219,4 @@
topo2PeerRegions: topo2PeerRegions
};
}]);
}());
})();
......
......@@ -22,7 +22,7 @@
(function () {
'use strict';
var Collection, Model, t2vs;
var Collection, Model;
function createHostCollection(data, region) {
......@@ -41,12 +41,10 @@
}
angular.module('ovTopo2')
.factory('Topo2HostService',
[
.factory('Topo2HostService', [
'Topo2Collection', 'Topo2NodeModel', 'Topo2ViewService',
function (_Collection_, _NodeModel_, classnames, _t2vs_) {
t2vs = _t2vs_;
Collection = _Collection_;
Model = _NodeModel_.extend({
......
......@@ -2,13 +2,7 @@
'use strict';
// injected refs
var $log,
ps,
sus,
gs,
ts,
fs,
flash;
var $log, ps, sus, gs, ts;
// api from topo
var api;
......@@ -24,10 +18,8 @@
// internal state
var onosInstances,
onosOrder,
oiShowMaster,
oiBox;
function addInstance(data) {
var id = data.id;
......@@ -51,30 +43,15 @@
}
}
function removeInstance(data) {
var id = data.id,
d = onosInstances[id];
if (d) {
var idx = fs.find(id, onosOrder);
if (idx >= 0) {
onosOrder.splice(idx, 1);
}
delete onosInstances[id];
updateInstances();
} else {
logicError('removeInstance lookup fail. ID = "' + id + '"');
}
}
// ==========================
function clickInst(d) {
var el = d3.select(this),
aff = el.classed('affinity');
if (!aff) {
setAffinity(el, d);
} else {
if (aff) {
cancelAffinity();
} else {
setAffinity(el, d);
}
}
......@@ -86,7 +63,6 @@
// suppress all elements except nodes whose master is this instance
api.showMastership(d.id);
oiShowMaster = true;
}
function cancelAffinity() {
......@@ -94,7 +70,6 @@
.classed('mastership affinity', false);
api.showMastership(null);
oiShowMaster = false;
}
function attachUiBadge(svg) {
......@@ -173,7 +148,6 @@
updAttr('ns', nSw(d.switches));
});
// operate on new onos instances
var entering = onoses.enter()
.append('div')
......@@ -235,9 +209,7 @@
onoses.exit().remove();
}
// ==========================
function logicError(msg) {
if (showLogicErrors) {
$log.warn('TopoInstService: ' + msg);
......@@ -251,23 +223,11 @@
onosInstances = {};
onosOrder = [];
oiShowMaster = false;
// we want to update the instances, each time the theme changes
ts.addListener(updateInstances);
}
function destroyInst() {
ts.removeListener(updateInstances);
ps.destroyPanel(idIns);
oiBox = null;
onosInstances = {};
onosOrder = [];
oiShowMaster = false;
}
function allInstances(data) {
$log.debug('Update all instances', data);
......@@ -281,16 +241,14 @@
angular.module('ovTopo2')
.factory('Topo2InstanceService',
['$log', 'PanelService', 'SvgUtilService', 'GlyphService',
'ThemeService', 'FnService', 'FlashService',
'ThemeService',
function (_$log_, _ps_, _sus_, _gs_, _ts_, _fs_, _flash_) {
function (_$log_, _ps_, _sus_, _gs_, _ts_) {
$log = _$log_;
ps = _ps_;
sus = _sus_;
gs = _gs_;
ts = _ts_;
fs = _fs_;
flash = _flash_;
return {
initInst: initInst,
......@@ -298,4 +256,4 @@
};
}]);
}());
})();
......
......@@ -24,12 +24,10 @@
var $log, sus, t2rs, t2d3, t2vs, t2ss;
var uplink, linkG, linkLabelG, numLinkLabelsG, nodeG, portLabelG;
var link, linkLabel, node;
var uplink, linkG, linkLabelG, nodeG;
var link, node;
var nodes, links, highlightedLink;
var force;
var highlightedLink;
// default settings for force layout
var defaultSettings = {
......@@ -51,9 +49,9 @@
linkStrength: {
// note: key is link.type
// range: {0.0 ... 1.0}
//direct: 1.0,
//optical: 1.0,
//hostLink: 1.0,
direct: 1.0,
optical: 1.0,
hostLink: 1.0,
_def_: 1.0
}
};
......@@ -79,23 +77,7 @@
var settings, // merged default settings and options
force, // force layout object
drag, // drag behavior handler
network = {
nodes: [],
links: [],
linksByDevice: {},
lookup: {},
revLinkToKey: {}
},
lu, // shorthand for lookup
rlk, // shorthand for revLinktoKey
showHosts = false, // whether hosts are displayed
showOffline = true, // whether offline devices are displayed
nodeLock = false, // whether nodes can be dragged or not (locked)
fTimer, // timer for delayed force layout
fNodesTimer, // timer for delayed nodes update
fLinksTimer, // timer for delayed links update
dim, // the dimensions of the force layout [w,h]
linkNums = []; // array of link number labels
nodeLock = false; // whether nodes can be dragged or not (locked)
var tickStuff = {
nodeAttr: {
......@@ -110,14 +92,6 @@
y1: function (d) { return d.get('position').y1; },
x2: function (d) { return d.get('position').x2; },
y2: function (d) { return d.get('position').y2; }
},
linkLabelAttr: {
transform: function (d) {
var lnk = tms.findLinkById(d.get('key'));
if (lnk) {
return t2d3.transformLabel(lnk.get('position'));
}
}
}
};
......@@ -129,14 +103,19 @@
linkG = forceG.append('g').attr('id', 'topo-links');
linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
numLinkLabelsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
forceG.append('g').attr('id', 'topo-numLinkLabels');
nodeG = forceG.append('g').attr('id', 'topo-nodes');
portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
forceG.append('g').attr('id', 'topo-portLabels');
link = linkG.selectAll('.link');
linkLabel = linkLabelG.selectAll('.linkLabel');
linkLabelG.selectAll('.linkLabel');
node = nodeG.selectAll('.node');
_svg_.on('mousemove', mouseMoveHandler);
}
function createForceLayout() {
force = d3.layout.force()
.size(t2vs.getDimensions())
.nodes(t2rs.regionNodes())
......@@ -148,10 +127,11 @@
.linkStrength(settings.linkStrength._def_)
.on('tick', tick);
drag = sus.createDragBehavior(force,
t2ss.selectObject, atDragEnd, dragEnabled, clickEnabled);
drag = sus.createDragBehavior(force,
t2ss.selectObject, atDragEnd, dragEnabled, clickEnabled);
_svg_.on('mousemove', mouseMoveHandler)
start();
update();
}
function zoomingOrPanning(ev) {
......@@ -188,9 +168,6 @@
.attr(tickStuff.linkAttr);
// t2d3.applyNumLinkLabels(linkNums, numLinkLabelsG);
}
if (linkLabel && linkLabel.size()) {
linkLabel.attr(tickStuff.linkLabelAttr);
}
}
function update() {
......@@ -210,7 +187,7 @@
.append('g')
.attr({
id: function (d) { return sus.safeId(d.get('id')); },
class: function (d) { return d.svgClassName() },
class: function (d) { return d.svgClassName(); },
transform: function (d) {
// Need to guard against NaN here ??
return sus.translate(d.node.x, d.node.y);
......@@ -227,10 +204,18 @@
entering.filter('.sub-region').each(t2d3.nodeEnter);
entering.filter('.host').each(t2d3.hostEnter);
// operate on both existing and new nodes:
// node.filter('.device').each(function (device) {
// t2d3.updateDeviceColors(device);
// });
// operate on exiting nodes:
// Note that the node is removed after 2 seconds.
// Sub element animations should be shorter than 2 seconds.
var exiting = node.exit()
.transition()
.duration(2000)
.style('opacity', 0)
.remove();
// exiting node specifics:
// exiting.filter('.host').each(t2d3.hostExit);
exiting.filter('.device').each(t2d3.nodeExit);
}
function _updateLinks() {
......@@ -241,16 +226,6 @@
link = linkG.selectAll('.link')
.data(regionLinks, function (d) { return d.get('key'); });
// operate on existing links:
link.each(function (d) {
// this is supposed to be an existing link, but we have observed
// occasions (where links are deleted and added rapidly?) where
// the DOM element has not been defined. So protect against that...
if (d.el) {
restyleLinkElement(d, true);
}
});
// operate on entering links:
var entering = link.enter()
.append('line')
......@@ -260,14 +235,14 @@
y1: function (d) { return d.get('position').y1; },
x2: function (d) { return d.get('position').x2; },
y2: function (d) { return d.get('position').y2; },
stroke: linkConfig['light'].inColor,
stroke: linkConfig.light.inColor,
'stroke-width': linkConfig.inWidth
});
entering.each(t2d3.linkEntering);
// operate on both existing and new links:
//link.each(...)
// link.each(...)
// add labels for how many links are in a thick line
// t2d3.applyNumLinkLabels(linkNums, numLinkLabelsG);
......@@ -278,7 +253,7 @@
// operate on exiting links:
link.exit()
.attr('stroke-dasharray', '3 3')
.attr('stroke', linkConfig['light'].outColor)
.attr('stroke', linkConfig.light.outColor)
.style('opacity', 0.5)
.transition()
.duration(1500)
......@@ -291,33 +266,20 @@
}
function calcPosition() {
var lines = this,
linkSrcId,
linkNums = [];
var lines = this;
lines.each(function (d) {
lines.each(function (d) {
if (d.get('type') === 'hostLink') {
d.set('position', getDefaultPos(d));
}
});
function normalizeLinkSrc(link) {
// ensure source device is consistent across set of links
// temporary measure until link modeling is refactored
if (!linkSrcId) {
linkSrcId = link.source.id;
return false;
}
return link.source.id !== linkSrcId;
}
lines.each(function (d) {
d.set('position', getDefaultPos(d));
});
}
function getDefaultPos(link) {
function getDefaultPos(link) {
return {
x1: link.get('source').x,
......@@ -333,7 +295,6 @@
}
}
function start() {
force.start();
}
......@@ -343,7 +304,6 @@
var mp = getLogicalMousePosition(this),
link = computeNearestLink(mp);
if (highlightedLink) {
highlightedLink.unenhance();
highlightedLink = null;
......@@ -363,11 +323,12 @@
tr = uplink.zoomer().translate(),
mx = (m[0] - tr[0]) / sc,
my = (m[1] - tr[1]) / sc;
return {x: mx, y: my};
return { x: mx, y: my };
}
function sq(x) { return x * x; }
function sq(x) {
return x * x;
}
function mdist(p, m) {
return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
......@@ -377,33 +338,6 @@
return dist / uplink.zoomer().scale();
}
function computeNearestNode(mouse) {
var proximity = prox(30),
nearest = null,
minDist,
regionNodes = t2rs.regionNodes();
if (regionNodes.length) {
minDist = proximity * 2;
regionNodes.forEach(function (d) {
var dist;
if (!api.showHosts() && d.class === 'host') {
return; // skip hidden hosts
}
dist = mdist({x: d.x, y: d.y}, mouse);
if (dist < minDist && dist < proximity) {
minDist = dist;
nearest = d;
}
});
}
return nearest;
}
function computeNearestLink(mouse) {
var proximity = prox(30),
nearest = null,
......@@ -418,11 +352,11 @@
y2 = line.y2,
x3 = mouse.x,
y3 = mouse.y,
k = ((y2-y1) * (x3-x1) - (x2-x1) * (y3-y1)) /
(sq(y2-y1) + sq(x2-x1)),
x4 = x3 - k * (y2-y1),
y4 = y3 + k * (x2-x1);
return {x:x4, y:y4};
k = ((y2 - y1) * (x3 - x1) - (x2 - x1) * (y3 - y1)) /
(sq(y2 - y1) + sq(x2 - x1)),
x4 = x3 - k * (y2 - y1),
y4 = y3 + k * (x2 - x1);
return { x: x4, y: y4 };
}
function lineHit(line, p, m) {
......@@ -476,11 +410,12 @@
return {
init: init,
createForceLayout: createForceLayout,
update: update,
start: start,
setDimensions: setDimensions
}
};
}
]
);
......
......@@ -23,7 +23,7 @@
'use strict';
var $log;
var Collection, Model, region, ts, sus;
var Collection, Model, ts, sus;
var linkLabelOffset = '0.35em';
......@@ -52,9 +52,6 @@
outWidth: 10
};
var defaultLinkType = 'direct',
nearDist = 15;
function createLink() {
var linkPoints = this.linkEndPoints(this.get('epA'), this.get('epB'));
......@@ -92,14 +89,18 @@
return box;
}
function isLinkOnline(node) {
return (node.get('nodeType') === 'region') ? true : node.get('online');
}
function linkEndPoints(srcId, dstId) {
var sourceNode = this.region.findNodeById(srcId)
var targetNode = this.region.findNodeById(dstId)
var sourceNode = this.region.findNodeById(srcId);
var targetNode = this.region.findNodeById(dstId);
if (!sourceNode || !targetNode) {
$log.error('Node(s) not on map for link:' + srcId + ':' + dstId);
//logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
// logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
return null;
}
......@@ -126,10 +127,13 @@
return true;
},
online: function () {
// TODO: remove next line
return true;
return both && (s && s.online) && (t && t.online);
var source = this.get('source'),
target = this.get('target'),
sourceOnline = isLinkOnline(source),
targetOnline = isLinkOnline(target);
return (sourceOnline) && (targetOnline);
},
enhance: function () {
var data = [],
......@@ -147,7 +151,17 @@
});
data.push(point);
var entering = d3.select('#topo-portLabels').selectAll('.portLabel')
if (this.get('portA')) {
point = this.locatePortLabel(1);
angular.extend(point, {
id: 'topo-port-src',
num: this.get('portA')
});
data.push(point);
}
var entering = d3.select('#topo-portLabels')
.selectAll('.portLabel')
.data(data).enter().append('g')
.classed('portLabel', true)
.attr('id', function (d) { return d.id; });
......@@ -171,7 +185,7 @@
this.el.classed('enhanced', false);
d3.select('#topo-portLabels').selectAll('.portLabel').remove();
},
locatePortLabel: function (link, src) {
locatePortLabel: function (src) {
var offset = 32,
pos = this.get('position'),
nearX = src ? pos.x1 : pos.x2,
......@@ -179,13 +193,15 @@
farX = src ? pos.x2 : pos.x1,
farY = src ? pos.y2 : pos.y1;
function dist(x, y) { return Math.sqrt(x*x + y*y); }
function dist(x, y) {
return Math.sqrt(x * x + y * y);
}
var dx = farX - nearX,
dy = farY - nearY,
k = offset / dist(dx, dy);
return {x: k * dx + nearX, y: k * dy + nearY};
return { x: k * dx + nearX, y: k * dy + nearY };
},
restyleLinkElement: function (immediate) {
// this fn's job is to look at raw links and decide what svg classes
......@@ -219,21 +235,19 @@
}
},
onEnter: function (el) {
var _this = this,
link = d3.select(el);
var link = d3.select(el);
this.el = link;
this.restyleLinkElement();
if (this.get('type') === 'hostLink') {
sus.visible(link, api.showHosts());
// sus.visible(link, api.showHosts());
}
}
});
var LinkCollection = Collection.extend({
model: LinkModel,
model: LinkModel
});
return new LinkCollection(data);
......@@ -241,7 +255,8 @@
angular.module('ovTopo2')
.factory('Topo2LinkService',
['$log', 'Topo2Collection', 'Topo2Model', 'ThemeService', 'SvgUtilService',
['$log', 'Topo2Collection', 'Topo2Model',
'ThemeService', 'SvgUtilService',
function (_$log_, _Collection_, _Model_, _ts_, _sus_) {
......
......@@ -42,7 +42,7 @@ Visualization of the topology in an SVG layer, using a D3 Force Layout.
return this.attributes[attr];
},
set: function(key, val, options) {
set: function (key, val, options) {
if (!key) {
return this;
......@@ -56,19 +56,21 @@ Visualization of the topology in an SVG layer, using a D3 Force Layout.
(attributes = {})[key] = val;
}
options || (options = {});
var opts = options || (options = {});
var unset = options.unset,
silent = options.silent,
var unset = opts.unset,
silent = opts.silent,
changes = [],
changing = this._changing;
changing = this._changing;
this._changing = true;
if (!changing) {
// NOTE: angular.copy causes issues in chrome
this._previousAttributes = Object.create(Object.getPrototypeOf(this.attributes));
this._previousAttributes = Object.create(
Object.getPrototypeOf(this.attributes)
);
this.changed = {};
}
......@@ -84,34 +86,38 @@ Visualization of the topology in an SVG layer, using a D3 Force Layout.
changes.push(index);
}
if (!angular.equals(previous[index], val)) {
changed[index] = val;
} else {
if (angular.equals(previous[index], val)) {
delete changed[index];
} else {
changed[index] = val;
}
unset ? delete current[index] : current[index] = val;
if (unset) {
delete current[index];
} else {
current[index] = val;
}
});
// Trigger all relevant attribute changes.
if (!silent) {
if (changes.length) {
this._pending = options;
this._pending = opts;
}
for (var i = 0; i < changes.length; i++) {
this.onChange(changes[i], this, current[changes[i]], options);
this.onChange(changes[i], this,
current[changes[i]], opts);
}
}
this._changing = false;
return this;
},
toJSON: function(options) {
return angular.copy(this.attributes)
},
toJSON: function (options) {
return angular.copy(this.attributes);
}
};
Model.extend = function (protoProps, staticProps) {
var parent = this;
......@@ -136,8 +142,7 @@ Visualization of the topology in an SVG layer, using a D3 Force Layout.
};
angular.module('ovTopo2')
.factory('Topo2Model',
[
.factory('Topo2Model', [
function () {
return Model;
}
......
......@@ -25,9 +25,8 @@
var randomService;
var fn;
//internal state;
var defaultLinkType = 'direct',
nearDist = 15;
// Internal state;
var nearDist = 15;
var devIconDim = 36,
labelPad = 10,
......@@ -42,9 +41,9 @@
dim = [800, 600],
xy;
// if the device contains explicit LONG/LAT data, use that to position
// If the device contains explicit LONG/LAT data, use that to position
if (setLongLat(node)) {
//indicate we want to update cached meta data...
// Indicate we want to update cached meta data...
return true;
}
......@@ -108,7 +107,7 @@
angular.module('ovTopo2')
.factory('Topo2NodeModel',
['Topo2Model', 'FnService', 'RandomService',
['Topo2Model', 'FnService', 'RandomService',
function (Model, _fn_, _RandomService_) {
randomService = _RandomService_;
......@@ -118,25 +117,28 @@
initialize: function () {
this.node = this.createNode();
},
onEnter: function () {}, // To be overridden by sub-class
onExit: function () {}, // To be overridden by sub-class
label: function () {
var props = this.get('props'),
id = this.get('id'),
friendlyName = props ? props.name : id,
labels = ['', friendlyName, id],
idx = (nodeLabelIndex < labels.length) ? nodeLabelIndex : 0;
nli = nodeLabelIndex,
idx = (nli < labels.length) ? nli : 0;
return labels[idx];
},
trimLabel: function(label) {
trimLabel: function (label) {
return (label && label.trim()) || '';
},
computeLabelWidth: function(el) {
computeLabelWidth: function (el) {
var text = el.select('text'),
box = text.node().getBBox();
box = text.node().getBBox();
return box.width + labelPad * 2;
},
addLabelElements: function(label) {
addLabelElements: function (label) {
var rect = this.el.append('rect');
var text = this.el.append('text').text(label)
.attr('text-anchor', 'left')
......@@ -146,12 +148,16 @@
return {
rect: rect,
text: text
}
};
},
svgClassName: function () {
return fn.classNames('node', this.nodeType, this.get('type'), {
online: this.get('online')
});
return fn.classNames('node',
this.nodeType,
this.get('type'),
{
online: this.get('online')
}
);
},
createNode: function () {
......
......@@ -23,32 +23,30 @@
'use strict';
// Injected Services
var $log, wss, t2sr, t2ds, t2hs, t2ls;
var Collection, Model;
var $log, t2sr, t2ds, t2hs, t2ls;
var Model;
//Internal
// Internal
var region;
function init() {
regions = {};
}
function init() {}
function addRegion(data) {
var RegionModel = Model.extend({
findNodeById: findNodeById
})
});
region = new RegionModel({
id: data.id,
layerOrder: data.layerOrder,
layerOrder: data.layerOrder
});
region.set({
subregions: t2sr.createSubRegionCollection(data.subregions, region),
devices: t2ds.createDeviceCollection(data.devices, region),
hosts: t2hs.createHostCollection(data.hosts, region),
links: t2ls.createLinkCollection(data.links, region),
links: t2ls.createLinkCollection(data.links, region)
});
angular.forEach(region.get('links').models, function (link) {
......@@ -58,18 +56,10 @@
$log.debug('Region: ', region);
}
function regionNodes() {
return [].concat(
region.get('devices').models,
region.get('hosts').models,
region.get('subregions').models
);
}
function findNodeById(id) {
// Remove /{port} from id if needed
var regex = new RegExp('^[^\/]*');
var regex = new RegExp('^[^/]*');
id = regex.exec(id)[0];
return region.get('devices').get(id) ||
......@@ -77,25 +67,37 @@
region.get('subregions').get(id);
}
function regionNodes() {
if (region) {
return [].concat(
region.get('devices').models,
region.get('hosts').models,
region.get('subregions').models
);
}
return [];
}
function regionLinks() {
return region.get('links').models;
return (region) ? region.get('links').models : [];
}
angular.module('ovTopo2')
.factory('Topo2RegionService',
['$log', 'WebSocketService', 'Topo2Model', 'Topo2SubRegionService', 'Topo2DeviceService',
'Topo2HostService', 'Topo2LinkService', 'Topo2Collection',
['$log', 'Topo2Model',
'Topo2SubRegionService', 'Topo2DeviceService',
'Topo2HostService', 'Topo2LinkService',
function (_$log_, _wss_, _Model_, _t2sr_, _t2ds_, _t2hs_, _t2ls_, _Collection_) {
function (_$log_, _Model_, _t2sr_, _t2ds_, _t2hs_, _t2ls_) {
$log = _$log_;
wss = _wss_;
Model = _Model_
Model = _Model_;
t2sr = _t2sr_;
t2ds = _t2ds_;
t2hs = _t2hs_;
t2ls = _t2ls_;
Collection = _Collection_;
return {
init: init,
......
......@@ -22,26 +22,18 @@
'use strict';
// internal state
var hovered, selections, selectOrder, consumeClick;
var consumeClick;
function selectObject(obj) {
var el = this,
nodeEv = el && el.tagName === 'g',
ev = d3.event.sourceEvent || {},
n;
console.log(el, nodeEv, ev, n);
}
function selectObject(obj) {}
function clickConsumed(x) {
var cc = consumeClick;
consumeClick = !!x;
consumeClick = Boolean(x);
return cc;
}
angular.module('ovTopo2')
.factory('Topo2SelectService',
[
.factory('Topo2SelectService', [
function () {
return {
......
......@@ -22,7 +22,7 @@
(function () {
'use strict';
var wss, is, sus, ts, t2vs;
var wss, is, sus;
var Collection, Model;
var remappedDeviceTypes = {
......@@ -31,22 +31,7 @@
// configuration
var devIconDim = 36,
labelPad = 10,
hostRadius = 14,
badgeConfig = {
radius: 12,
yoff: 5,
gdelta: 10
},
halfDevIcon = devIconDim / 2,
devBadgeOff = { dx: -halfDevIcon, dy: -halfDevIcon },
hostBadgeOff = { dx: -hostRadius, dy: -hostRadius },
status = {
i: 'badgeInfo',
w: 'badgeWarn',
e: 'badgeError'
},
deviceLabelIndex = 0;
halfDevIcon = devIconDim / 2;
function createSubRegionCollection(data, region) {
......@@ -67,22 +52,20 @@
y: -dim / 2,
width: dim + labelWidth,
height: dim
}
};
}
angular.module('ovTopo2')
.factory('Topo2SubRegionService',
['WebSocketService', 'Topo2Collection', 'Topo2NodeModel', 'IconService', 'SvgUtilService',
'ThemeService', 'Topo2ViewService',
['WebSocketService', 'Topo2Collection', 'Topo2NodeModel',
'IconService', 'SvgUtilService', 'ThemeService', 'Topo2ViewService',
function (_wss_, _Collection_, _NodeModel_, _is_, _sus_, _ts_, classnames, _t2vs_) {
function (_wss_, _c_, _NodeModel_, _is_, _sus_, _ts_, _t2vs_) {
wss = _wss_;
t2vs = _t2vs_;
is = _is_;
sus = _sus_;
ts = _ts_;
Collection = _Collection_;
Collection = _c_;
Model = _NodeModel_.extend({
initialize: function () {
......
......@@ -39,7 +39,7 @@
return {
newDim: newDim,
getDimensions: getDimensions
}
};
}
]
);
......
......@@ -15,8 +15,10 @@
"license": "Apache 2.0",
"devDependencies": {
"browser-sync": "^2.12.8",
"eslint": "^3.4.0",
"eslint-config-google": "^0.6.0",
"express": "^4.14.0",
"parallelshell": "^2.0.0",
"serve-static": "^1.10.2",
"express": "^4.14.0"
"serve-static": "^1.10.2"
}
}
......