aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js')
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js338
1 files changed, 338 insertions, 0 deletions
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js
new file mode 100644
index 00000000..38f3a6c3
--- /dev/null
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoLink.js
@@ -0,0 +1,338 @@
+/*
+ * Copyright 2015 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 Link Module.
+ Functions for highlighting/selecting links
+ */
+
+(function () {
+ 'use strict';
+
+ // injected refs
+ var $log, fs, sus, ts, flash, tss, tps;
+
+ // internal state
+ var api,
+ td3,
+ network,
+ showPorts = true, // enable port highlighting by default
+ enhancedLink = null, // the link over which the mouse is hovering
+ selectedLink = null; // the link which is currently selected
+
+ // SVG elements;
+ var svg;
+
+
+ // ======== ALGORITHM TO FIND LINK CLOSEST TO MOUSE ========
+
+ function getLogicalMousePosition(container) {
+ var m = d3.mouse(container),
+ sc = api.zoomer.scale(),
+ tr = api.zoomer.translate(),
+ mx = (m[0] - tr[0]) / sc,
+ my = (m[1] - tr[1]) / sc;
+ return {x: mx, y: my};
+ }
+
+
+ function sq(x) { return x * x; }
+
+ function mdist(p, m) {
+ return Math.sqrt(sq(p.x - m.x) + sq(p.y - m.y));
+ }
+
+ function prox(dist) {
+ return dist / api.zoomer.scale();
+ }
+
+ function computeNearestNode(mouse) {
+ var proximity = prox(30),
+ nearest = null,
+ minDist;
+
+ if (network.nodes.length) {
+ minDist = proximity * 2;
+
+ network.nodes.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,
+ minDist;
+
+ function pdrop(line, mouse) {
+ var x1 = line.x1,
+ y1 = line.y1,
+ x2 = line.x2,
+ 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};
+ }
+
+ function lineHit(line, p, m) {
+ if (p.x < line.x1 && p.x < line.x2) return false;
+ if (p.x > line.x1 && p.x > line.x2) return false;
+ if (p.y < line.y1 && p.y < line.y2) return false;
+ if (p.y > line.y1 && p.y > line.y2) return false;
+ // line intersects, but are we close enough?
+ return mdist(p, m) <= proximity;
+ }
+
+ if (network.links.length) {
+ minDist = proximity * 2;
+
+ network.links.forEach(function (d) {
+ if (!api.showHosts() && d.type() === 'hostLink') {
+ return; // skip hidden host links
+ }
+
+ var line = d.position,
+ point = pdrop(line, mouse),
+ hit = lineHit(line, point, mouse),
+ dist;
+
+ if (hit) {
+ dist = mdist(point, mouse);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = d;
+ }
+ }
+ });
+ }
+ return nearest;
+ }
+
+ function enhanceLink(ldata) {
+ // if the new link is same as old link, do nothing
+ if (enhancedLink && ldata && enhancedLink.key === ldata.key) return;
+
+ // first, unenhance the currently enhanced link
+ if (enhancedLink) {
+ unenhance(enhancedLink);
+ }
+ enhancedLink = ldata;
+ if (enhancedLink) {
+ enhance(enhancedLink);
+ }
+ }
+
+ function unenhance(d) {
+ // guard against link element not set
+ if (d.el) {
+ d.el.classed('enhanced', false);
+ }
+ api.portLabelG().selectAll('.portLabel').remove();
+ }
+
+ function enhance(d) {
+ var data = [],
+ point;
+
+ // guard against link element not set
+ if (!d.el) return;
+
+ d.el.classed('enhanced', true);
+
+ // Define port label data objects.
+ // NOTE: src port is absent in the case of host-links.
+
+ point = locatePortLabel(d);
+ angular.extend(point, {
+ id: 'topo-port-tgt',
+ num: d.tgtPort
+ });
+ data.push(point);
+
+ if (d.srcPort) {
+ point = locatePortLabel(d, 1);
+ angular.extend(point, {
+ id: 'topo-port-src',
+ num: d.srcPort
+ });
+ data.push(point);
+ }
+
+ td3.applyPortLabels(data, api.portLabelG());
+ }
+
+ function locatePortLabel(link, src) {
+ var offset = 32,
+ pos = link.position,
+ nearX = src ? pos.x1 : pos.x2,
+ nearY = src ? pos.y1 : pos.y2,
+ farX = src ? pos.x2 : pos.x1,
+ farY = src ? pos.y2 : pos.y1;
+
+ 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};
+ }
+
+ function selectLink(ldata) {
+ // if the new link is same as old link, do nothing
+ if (selectedLink && ldata && selectedLink.key === ldata.key) return;
+
+ // make sure no nodes are selected
+ tss.deselectAll();
+
+ // first, unenhance the currently enhanced link
+ if (selectedLink) {
+ unselLink(selectedLink);
+ }
+ selectedLink = ldata;
+ if (selectedLink) {
+ selLink(selectedLink);
+ }
+ }
+
+ function unselLink(d) {
+ // guard against link element not set
+ if (d.el) {
+ d.el.classed('selected', false);
+ }
+ }
+
+ function selLink(d) {
+ // guard against link element not set
+ if (!d.el) return;
+
+ d.el.classed('selected', true);
+
+ tps.displayLink(d);
+ tps.displaySomething();
+ }
+
+ // ====== MOUSE EVENT HANDLERS ======
+
+ function mouseMoveHandler() {
+ var mp = getLogicalMousePosition(this),
+ link = computeNearestLink(mp);
+ enhanceLink(link);
+ }
+
+ function mouseClickHandler() {
+ var mp, link, node;
+
+ if (!tss.clickConsumed()) {
+ mp = getLogicalMousePosition(this);
+ node = computeNearestNode(mp);
+ if (node) {
+ $log.debug('found nearest node:', node.labels[1]);
+ tss.selectObject(node);
+ } else {
+ link = computeNearestLink(mp);
+ selectLink(link);
+ }
+ }
+ }
+
+
+ // ======================
+
+ function togglePorts(x) {
+ var kev = (x === 'keyev'),
+ on = kev ? !showPorts : !!x,
+ what = on ? 'Enable' : 'Disable',
+ handler = on ? mouseMoveHandler : null;
+
+ showPorts = on;
+
+ if (!on) {
+ enhanceLink(null);
+ }
+ svg.on('mousemove', handler);
+ flash.flash(what + ' port highlighting');
+ return on;
+ }
+
+ function deselectLink() {
+ if (selectedLink) {
+ unselLink(selectedLink);
+ selectedLink = null;
+ return true;
+ }
+ return false;
+ }
+
+ // ==========================
+ // Module definition
+
+ angular.module('ovTopo')
+ .factory('TopoLinkService',
+ ['$log', 'FnService', 'SvgUtilService', 'ThemeService', 'FlashService',
+ 'TopoSelectService', 'TopoPanelService',
+
+ function (_$log_, _fs_, _sus_, _ts_, _flash_, _tss_, _tps_) {
+ $log = _$log_;
+ fs = _fs_;
+ sus = _sus_;
+ ts = _ts_;
+ flash = _flash_;
+ tss = _tss_;
+ tps = _tps_;
+
+ function initLink(_api_, _td3_) {
+ api = _api_;
+ td3 = _td3_;
+ svg = api.svg;
+ network = api.network;
+ if (showPorts && !fs.isMobile()) {
+ svg.on('mousemove', mouseMoveHandler);
+ }
+ svg.on('click', mouseClickHandler);
+ }
+
+ function destroyLink() {
+ // unconditionally remove any event handlers
+ svg.on('mousemove', null);
+ svg.on('click', null);
+ }
+
+ return {
+ initLink: initLink,
+ destroyLink: destroyLink,
+ togglePorts: togglePorts,
+ deselectLink: deselectLink
+ };
+ }]);
+}());