http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/plugin/js/qdrTopology.js ---------------------------------------------------------------------- diff --git a/console/plugin/js/qdrTopology.js b/console/plugin/js/qdrTopology.js deleted file mode 100644 index c8921ff..0000000 --- a/console/plugin/js/qdrTopology.js +++ /dev/null @@ -1,1871 +0,0 @@ -/* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you 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. -*/ -/** - * @module QDR - */ -var QDR = (function (QDR) { - - /** - * @method SettingsController - * @param $scope - * @param QDRServer - * - * Controller that handles the QDR settings page - */ - - /** - * @function NavBarController - * - * @param $scope - * @param workspace - * - * The controller for this plugin's navigation bar - * - */ - - QDR.module.controller("QDR.TopologyController", ['$scope', '$rootScope', 'uiGridConstants', 'QDRService', '$uibModal', '$location', '$timeout', - function($scope, $rootScope, uiGridConstants, QDRService, $uibModal, $location, $timeout) { - - QDR.log.debug("started QDR.TopologyController with location.url: " + $location.url()); - var urlPrefix = window.location.pathname; - - $scope.attributes = []; - $scope.connAttributes = []; - $scope.topoForm = "general"; - $scope.topoFormSelected = ""; - $scope.addingNode = { - step: 0, - hasLink: false, - trigger: '' - }; // shared object about the node that is be $scope.topoForm = "general"; - - var generalCellTemplate = '<div class="ngCellText"><span title="{{row.entity.description}}">{{row.entity.attributeName}}</span></div>'; - - $scope.isGeneral = function () { - //QDR.log.debug("$scope.topoForm=" + $scope.topoForm) - return $scope.topoForm === 'general'; - }; - $scope.isConnections = function () { - //QDR.log.debug("$scope.topoForm=" + $scope.topoForm) - return $scope.topoForm === 'connections'; - }; - $scope.isAddNode = function () { - //QDR.log.debug("$scope.topoForm=" + $scope.topoForm) - return $scope.topoForm === 'addNode'; - } - - $scope.getTableHeight = function (rows) { - return {height: (rows.length * 30) + "px"}; - } - $scope.isSelected = function () { - return ($scope.topoFormSelected != ""); - } - - $scope.cancel = function () { - $scope.addingNode.step = 0; - } - $scope.editNewRouter = function () { - $scope.addingNode.trigger = 'editNode'; - } - - $scope.topoGridOptions = { - data: 'attributes', - enableColumnResize: true, - enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER, - enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER, - multiSelect: false, - columnDefs: [ - { - field: 'attributeName', - displayName: 'Attribute', - cellTemplate: generalCellTemplate - }, - { - field: 'attributeValue', - displayName: 'Value' - } - ] - }; - $scope.topoConnOptions = angular.copy($scope.topoGridOptions); - $scope.topoConnOptions.data = 'connAttributes'; - var NewRouterName = "__NEW__"; - // mouse event vars - var selected_node = null, - selected_link = null, - mousedown_link = null, - mousedown_node = null, - mouseup_node = null, - initial_mouse_down_position = null; - - $scope.schema = "Not connected"; - - $scope.modes = [ - {title: 'Topology view', name: 'Diagram', right: false}, - /* {title: '3D Globe view', name: 'Globe', right: false}, */ - /* {title: 'Add a new router node', name: 'Add Router', right: true} */ - ]; - $scope.mode = "Diagram"; - $scope.contextNode = null; // node that is associated with the current context menu - - $scope.isModeActive = function (name) { - if ((name == 'Add Router' || name == 'Diagram') && $scope.addingNode.step > 0) - return true; - return ($scope.mode == name); - } - $scope.selectMode = function (name) { - if (name == "Add Router") { - name = 'Diagram'; - if ($scope.addingNode.step > 0) { - $scope.topoForm = 'general' - $scope.topoFormSelected = ''; - $scope.addingNode.step = 0; - } else { - // start adding node mode - $scope.addingNode.step = 1; - } - } else { - $scope.topoForm = 'general' - $scope.topoFormSelected = ''; - $scope.addingNode.step = 0; - } - - $scope.mode = name; - } - $scope.$watch(function () {return $scope.addingNode.step}, function (newValue, oldValue) { - if (newValue == 0 && oldValue != 0) { - // we are cancelling the add - - // find the New node - nodes.every(function (n, i) { - // for the placeholder node, the key will be __internal__ - if (QDRService.nameFromId(n.key) == '__internal__') { - var newLinks = links.filter(function (e, i) { - return e.source.id == n.id || e.target.id == n.id; - }) - // newLinks is an array of links to remove - newLinks.map(function (e) { - links.splice(links.indexOf(e), 1); - }) - // i is the index of the node to remove - nodes.splice(i, 1); - force.nodes(nodes).links(links).start(); - restart(false); - return false; // stop looping - } - return true; - }) - $scope.topoForm = 'general' - $scope.topoFormSelected = ''; - } else if (newValue > 0) { - // we are starting the add mode - $scope.topoForm = 'addNode'; - $scope.topoFormSelected = 'addNode'; - - resetMouseVars(); - selected_node = null; - selected_link = null; - // add a new node - var id = "amqp:/_topo/0/__internal__/$management"; - var x = radiusNormal * 4; - var y = x;; - if (newValue > 1) { // add at current mouse position - var offset = jQuery('#topology').offset(); - x = mouseX - offset.left + $(document).scrollLeft(); - y = mouseY - offset.top + $(document).scrollTop();; - } - NewRouterName = genNewName(); - nodes.push( aNode(id, NewRouterName, "inter-router", undefined, nodes.length, x, y, undefined, true) ); - force.nodes(nodes).links(links).start(); - restart(false); - } - - }) - $scope.isRight = function (mode) { - return mode.right; - } - - - // generate unique name for router and containerName - var genNewName = function () { - var nodeInfo = QDRService.topology.nodeInfo(); - var nameIndex = 1; - var newName = "R." + nameIndex; - - var names = []; - for (key in nodeInfo) { - var node = nodeInfo[key]; - var router = node['.router']; - var attrNames = router.attributeNames; - var name = QDRService.valFor(attrNames, router.results[0], 'routerId') - if (!name) - name = QDRService.valFor(attrNames, router.results[0], 'name') - names.push(name); - } - - while (names.indexOf(newName) >= 0) { - newName = "R." + nameIndex++; - } - return newName; - } - - $scope.$watch(function () {return $scope.addingNode.trigger}, function (newValue, oldValue) { - if (newValue == 'editNode') { - $scope.addingNode.trigger = ""; - editNode(); - } - }) - - function editNode() { - doAddDialog(NewRouterName); - }; - $scope.reverseLink = function () { - if (!mousedown_link) - return; - var d = mousedown_link; - var tmp = d.left; - d.left = d.right;; - d.right = tmp; - restart(false); - tick(); - } - $scope.removeLink = function () { - if (!mousedown_link) - return; - var d = mousedown_link; - links.every( function (l, i) { - if (l.source.id == d.source.id && l.target.id == d.target.id) { - links.splice(i, 1); - force.links(links).start(); - return false; // exit the 'every' loop - } - return true; - }); - restart(false); - tick(); - } - $scope.setFixed = function (b) { - if ($scope.contextNode) { - $scope.contextNode.fixed = b; - } - restart(); - } - $scope.isFixed = function () { - if (!$scope.contextNode) - return false; - return ($scope.contextNode.fixed & 0b1); - } - - var mouseX, mouseY; - // event handlers for popup context menu - $(document).mousemove(function (e) { - mouseX = e.clientX; - mouseY = e.clientY; - }); - $(document).mousemove(); - $(document).click(function (e) { - $scope.contextNode = null; - $(".contextMenu").fadeOut(200); - }); - - - // set up SVG for D3 - var width, height; - var tpdiv = $('#topology'); - var colors = {'inter-router': "#EAEAEA", 'normal': "#F0F000", 'on-demand': '#00F000'}; - var gap = 5; - var radii = {'inter-router': 25, 'normal': 15, 'on-demand': 15}; - var radius = 25; - var radiusNormal = 15; - width = tpdiv.width() - gap; - height = $(document).height() - gap; - - var svg; - var force; - var animate = false; // should the force graph organize itself when it is displayed - var path, circle; - var savedKeys = {}; - - // set up initial nodes and links - // - nodes are known by 'id', not by index in array. - // - selected edges are indicated on the node (as a bold red circle). - // - links are always source < target; edge directions are set by 'left' and 'right'. - var nodes = []; - var links = []; - - var aNode = function (id, name, nodeType, nodeInfo, nodeIndex, x, y, resultIndex, fixed) { - var containerName; - if (nodeInfo) { - var node = nodeInfo[id]; - if (node) { - containerName = node['.container'].results[0][0]; - } - } - return { key: id, - name: name, - nodeType: nodeType, - containerName: containerName, - x: x, - y: y, - id: nodeIndex, - resultIndex: resultIndex, - fixed: fixed, - cls: name == NewRouterName ? 'temp' : '' - }; - }; - - - var initForm = function (attributes, results, entityType, formFields) { - - while(formFields.length > 0) { - // remove all existing attributes - formFields.pop(); - } - - for (var i=0; i<attributes.length; ++i) { - var name = attributes[i]; - var val = results[i]; - var desc = ""; - if (entityType.attributes[name]) - if (entityType.attributes[name].description) - desc = entityType.attributes[name].description; - - formFields.push({'attributeName': name, 'attributeValue': val, 'description': desc}); - } - } - - var cities = ["Raleigh"]; - var possibleCities = ["Boston","Tel Aviv-Yafo", "Brno", "Toronto", "Beijing", , "Ashburn", "Raleigh"] - //var drag; - // create an bare svg element and - // initialize the nodes and links array from the QDRService.topology._nodeInfo object - var initForceGraph = function () { - //QDR.log.debug("initForceGraph called"); - nodes = []; - links = []; - - svg = d3.select('#topology') - .append('svg') - .attr("id", "SVG_ID") - .attr('width', width) - .attr('height', height) - .on("contextmenu", function(d) { - if (d3.event.defaultPrevented) - return; - d3.event.preventDefault(); - if ($scope.addingNode.step != 0) - return; - if (d3.select('#svg_context_menu').style('display') !== 'block') - $(document).click(); - d3.select('#svg_context_menu') - .style('left', (mouseX + $(document).scrollLeft()) + "px") - .style('top', (mouseY + $(document).scrollTop()) + "px") - .style('display', 'block'); - }) - .on('click', function (d) { - d3.select("#crosssection").style("display","none"); - d3.select("#crosssection svg").remove(); - - }); - - // mouse event vars - selected_node = null; - selected_link = null; - mousedown_link = null; - mousedown_node = null; - mouseup_node = null; - - // initialize the list of nodes - var yInit = 10; - var nodeInfo = QDRService.topology.nodeInfo(); - var nodeCount = Object.keys(nodeInfo).length; - for (var id in nodeInfo) { - var name = QDRService.nameFromId(id); - // if we have any new nodes, animate the force graph to position them - var position = angular.fromJson(localStorage[name]); - if (!angular.isDefined(position)) { - animate = true; - position = {x: width / 4 + ((width / 2)/nodeCount) * nodes.length, - y: height / 2 + yInit, - fixed: false}; - } - nodes.push( aNode(id, name, "inter-router", nodeInfo, nodes.length, position.x, position.y, undefined, position.fixed) ); - yInit *= -1; - //QDR.log.debug("adding node " + nodes.length-1); - } - - // initialize the list of links - var source = 0; - var client = 1; - cities = ["Raleigh"]; - for (var id in nodeInfo) { - var onode = nodeInfo[id]; - var conns = onode['.connection'].results; - var attrs = onode['.connection'].attributeNames; - - for (var j = 0; j < conns.length; j++) { - var role = QDRService.valFor(attrs, conns[j], "role"); - var dir = QDRService.valFor(attrs, conns[j], "dir"); - if (role == "inter-router") { - var connId = QDRService.valFor(attrs, conns[j], "container"); - var target = getContainerIndex(connId); - if (target >= 0) - getLink(source, target, dir); - } else if (role == "normal" || role == "on-demand") { - // not a router, but an external client - //QDR.log.debug("found an external client for " + id); - var name = QDRService.nameFromId(id) + "." + client; - //QDR.log.debug("external client name is " + name + " and the role is " + role); - var parent = getNodeIndex(QDRService.nameFromId(id)); - //QDR.log.debug("external client parent is " + parent); - - // if we have any new clients, animate the force graph to position them - var position = angular.fromJson(localStorage[name]); - if (!angular.isDefined(position)) { - animate = true; - position = {x: nodes[parent].x + 40 + Math.sin(Math.PI/2 * client), - y: nodes[parent].y + 40 + Math.cos(Math.PI/2 * client), - fixed: false}; - } - //QDR.log.debug("adding node " + nodeIndex); - nodes.push( aNode(id, name, role, nodeInfo, nodes.length, position.x, position.y, j, position.fixed) ); - // now add a link - getLink(parent, nodes.length-1, dir); - client++; - -/* - var container = QDRService.valFor(attrs, conns[j], "container"); - var parts = container.split('.') - if (parts.length) { - var city = parts[parts.length-1] - if (city === 'TelAvivYafo') - city = 'Tel Aviv-Yafo' - if (possibleCities.indexOf(city) > -1) { - if (cities.indexOf(city) == -1) { - cities.push(city); - } - } - } else { - // there was no city - var user = QDRService.valFor(attrs, conns[j], "user"); - city = 'Boston' - if (cities.indexOf(city) == -1 && role == 'normal' && user != "anonymous") { - cities.push(city); - } - - } -*/ - } - } - source++; - } - - $scope.schema = QDRService.schema; - // add a row for each attribute in .router attributeNames array - for (var id in nodeInfo) { - var onode = nodeInfo[id]; - - initForm(onode['.connection'].attributeNames, onode['.connection'].results[0], QDRService.schema.entityTypes.connection, $scope.connAttributes); - initForm(onode['.router'].attributeNames, onode['.router'].results[0], QDRService.schema.entityTypes.router, $scope.attributes); - - break; - } - // init D3 force layout - force = d3.layout.force() - .nodes(nodes) - .links(links) - .size([width, height]) - .linkDistance(function(d) { return d.target.nodeType === 'inter-router' ? 150 : 65 }) - .charge(-1800) - .friction(.10) - .gravity(0.0001) - .on('tick', tick) - .start() - - //drag = force.drag() - // .on("dragstart", dragstart); - - svg.append("svg:defs").selectAll('marker') - .data(["end-arrow", "end-arrow-selected"]) // Different link/path types can be defined here - .enter().append("svg:marker") // This section adds in the arrows - .attr("id", String) - .attr("viewBox", "0 -5 10 10") - //.attr("refX", 25) - .attr("markerWidth", 4) - .attr("markerHeight", 4) - .attr("orient", "auto") - .append("svg:path") - .attr('d', 'M 0 -5 L 10 0 L 0 5 z') - - svg.append("svg:defs").selectAll('marker') - .data(["start-arrow", "start-arrow-selected"]) // Different link/path types can be defined here - .enter().append("svg:marker") // This section adds in the arrows - .attr("id", String) - .attr("viewBox", "0 -5 10 10") - .attr("refX", 5) - .attr("markerWidth", 4) - .attr("markerHeight", 4) - .attr("orient", "auto") - .append("svg:path") - .attr('d', 'M 10 -5 L 0 0 L 10 5 z'); - - // handles to link and node element groups - path = svg.append('svg:g').selectAll('path'), - circle = svg.append('svg:g').selectAll('g'); - - force.on('end', function() { - //QDR.log.debug("force end called"); - circle - .attr('cx', function(d) { - localStorage[d.name] = angular.toJson({x: d.x, y: d.y, fixed: d.fixed}); - return d.x; }); - }); - - // app starts here - restart(false); - force.start(); - } -/* - function dragstart(d) { - d3.select(this).classed("fixed", d.fixed = true); - } - - function dblclick(d) { - d3.select(this).classed("fixed", d.fixed = false); - } -*/ - var initGlobe = function (clients) { - d3.select(window) - .on("mousemove", mousemove) - .on("mouseup", mouseup); - - var width = 960, - height = 500; - - var proj = d3.geo.orthographic() - .scale(220) - .translate([width / 2, height / 2]) - .clipAngle(90); - - var path = d3.geo.path().projection(proj).pointRadius(1.5); - - var links = [], - arcLines = []; - - var graticule = d3.geo.graticule(); - d3.select("#geology svg").remove(); - var svg = d3.select("#geology").append("svg") - .attr("width", width) - .attr("height", height) - .on("mousedown", mousedown); - - queue() - .defer(d3.json, "plugin/data/world-110m.json") - .defer(d3.json, "plugin/data/places1.json") - .await(ready); - - function ready(error, world, places) { - var ocean_fill = svg.append("defs").append("radialGradient") - .attr("id", "ocean_fill") - .attr("cx", "75%") - .attr("cy", "25%"); - ocean_fill.append("stop").attr("offset", "5%").attr("stop-color", "#fff"); - ocean_fill.append("stop").attr("offset", "100%").attr("stop-color", "#eef"); - - var globe_highlight = svg.append("defs").append("radialGradient") - .attr("id", "globe_highlight") - .attr("cx", "75%") - .attr("cy", "25%"); - globe_highlight.append("stop") - .attr("offset", "5%").attr("stop-color", "#ffd") - .attr("stop-opacity","0.6"); - globe_highlight.append("stop") - .attr("offset", "100%").attr("stop-color", "#ba9") - .attr("stop-opacity","0.1"); - - var globe_shading = svg.append("defs").append("radialGradient") - .attr("id", "globe_shading") - .attr("cx", "55%") - .attr("cy", "45%"); - globe_shading.append("stop") - .attr("offset","30%").attr("stop-color", "#fff") - .attr("stop-opacity","0") - globe_shading.append("stop") - .attr("offset","100%").attr("stop-color", "#505962") - .attr("stop-opacity","0.2") - - var drop_shadow = svg.append("defs").append("radialGradient") - .attr("id", "drop_shadow") - .attr("cx", "50%") - .attr("cy", "50%"); - drop_shadow.append("stop") - .attr("offset","20%").attr("stop-color", "#000") - .attr("stop-opacity",".5") - drop_shadow.append("stop") - .attr("offset","100%").attr("stop-color", "#000") - .attr("stop-opacity","0") - - svg.append("ellipse") - .attr("cx", 440).attr("cy", 450) - .attr("rx", proj.scale()*.90) - .attr("ry", proj.scale()*.25) - .attr("class", "noclicks") - .style("fill", "url("+urlPrefix+"#drop_shadow)"); - - svg.append("circle") - .attr("cx", width / 2).attr("cy", height / 2) - .attr("r", proj.scale()) - .attr("class", "noclicks") - .style("fill", "url("+urlPrefix+"#ocean_fill)"); - - svg.append("path") - .datum(topojson.object(world, world.objects.land)) - .attr("class", "land noclicks") - .attr("d", path); - - svg.append("path") - .datum(graticule) - .attr("class", "graticule noclicks") - .attr("d", path); - - svg.append("circle") - .attr("cx", width / 2).attr("cy", height / 2) - .attr("r", proj.scale()) - .attr("class","noclicks") - .style("fill", "url("+urlPrefix+"#globe_highlight)"); - - svg.append("circle") - .attr("cx", width / 2).attr("cy", height / 2) - .attr("r", proj.scale()) - .attr("class","noclicks") - .style("fill", "url("+urlPrefix+"#globe_shading)"); - - var filtered = places.features.filter( function (feature) { - return clients.indexOf(feature.properties.NAME) > -1 - }) - svg.append("g").attr("class","points") - .selectAll("text").data(filtered) - .enter().append("path") - .attr("class", "point") - .attr("d", path); - - svg.append("g").attr("class","labels") - .selectAll("text").data(filtered) - .enter().append("text") - .attr("class", "label") - .text(function(d) { return d.properties.NAME }) - - position_labels(); - - // spawn links between cities as source/target coord pairs - places.features.forEach(function(a, i) { - if (clients.indexOf(a.properties.NAME) > -1) { - places.features.forEach(function(b, j) { - if (b.properties.NAME === 'Raleigh') { - if (j > i) { // avoid duplicates - links.push({ - source: a.geometry.coordinates, - target: b.geometry.coordinates - }); - } - } - }); - } - }); - - // build geoJSON features from links array - links.forEach(function(e,i,a) { - var feature = { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [e.source,e.target] }} - arcLines.push(feature) - }) - - svg.append("g").attr("class","arcs") - .selectAll("path").data(arcLines) - .enter().append("path") - .attr("class","arc") - .attr("d",path) - refresh(); - } - - function position_labels() { - var centerPos = proj.invert([width/2,height/2]); - - var arc = d3.geo.greatArc(); - - svg.selectAll(".label") - .attr("transform", function(d) { - var loc = proj(d.geometry.coordinates), - x = loc[0], - y = loc[1]; - var offset = x < width/2 ? -5 : 5; - return "translate(" + (x+offset) + "," + (y-2) + ")" - }) - .style("display",function(d) { - var d = arc.distance({source: d.geometry.coordinates, target: centerPos}); - return (d > 1.57) ? 'none' : 'inline'; - }) - } - - function refresh() { - svg.selectAll(".land").attr("d", path); - svg.selectAll(".point").attr("d", path); - svg.selectAll(".graticule").attr("d", path); - svg.selectAll(".arc").attr("d", path); - position_labels(); - } - - // modified from http://bl.ocks.org/1392560 - var m0, o0; - o0 = angular.fromJson(localStorage["QDR.rotate"]); - if (o0) - proj.rotate(o0); - - function mousedown() { - m0 = [d3.event.pageX, d3.event.pageY]; - o0 = proj.rotate(); - d3.event.preventDefault(); - } - function mousemove() { - if (m0) { - var m1 = [d3.event.pageX, d3.event.pageY] - , o1 = [o0[0] + (m1[0] - m0[0]) / 6, o0[1] + (m0[1] - m1[1]) / 6]; - o1[1] = o1[1] > 30 ? 30 : - o1[1] < -30 ? -30 : - o1[1]; - proj.rotate(o1); - refresh(); - } - } - function mouseup() { - if (m0) { - mousemove(); - m0 = null; - localStorage["QDR.rotate"] = angular.toJson(proj.rotate()); - } - } - } - - // called when we mouseover a node - // we need to update the table - function updateNodeForm (d) { - //QDR.log.debug("update form info for "); - //console.dump(d); - var nodeInfo = QDRService.topology.nodeInfo(); - var onode = nodeInfo[d.key]; - if (onode) { - var nodeResults = onode['.router'].results[0]; - var nodeAttributes = onode['.router'].attributeNames; - - for (var i=0; i<$scope.attributes.length; ++i) { - var idx = nodeAttributes.indexOf($scope.attributes[i].attributeName); - if (idx > -1) { - if ($scope.attributes[i].attributeValue != nodeResults[idx]) { - // highlight the changed data - $scope.attributes[i].attributeValue = nodeResults[idx]; - - } - } - } - } - $scope.topoForm = "general"; - $scope.$apply(); - } - - function updateConnForm (d, resultIndex) { - var nodeInfo = QDRService.topology.nodeInfo(); - var onode = nodeInfo[d.key]; - if (onode && onode['.connection']) { - var nodeResults = onode['.connection'].results[resultIndex]; - var nodeAttributes = onode['.connection'].attributeNames; - - for (var i=0; i<$scope.connAttributes.length; ++i) { - var idx = nodeAttributes.indexOf($scope.connAttributes[i].attributeName); - if (idx > -1) { - try { - if ($scope.connAttributes[i].attributeValue != nodeResults[idx]) { - // highlight the changed data - $scope.connAttributes[i].attributeValue = nodeResults[idx]; - - } - } catch (err) { - QDR.log.error("error updating form" + err) - } - } - } - } - $scope.topoForm = "connections"; - $scope.$apply(); - } - - - function getContainerIndex(_id) { - var nodeIndex = 0; - var nodeInfo = QDRService.topology.nodeInfo(); - for (var id in nodeInfo) { - var node = nodeInfo[id]; - if (node['.container'].results[0][0] == _id) - return nodeIndex; - nodeIndex++ - } - QDR.log.warn("unable to find containerIndex for " + _id); - return -1; - } - - function getNodeIndex (_id) { - var nodeIndex = 0; - var nodeInfo = QDRService.topology.nodeInfo(); - for (var id in nodeInfo) { - if (QDRService.nameFromId(id) == _id) return nodeIndex; - nodeIndex++ - } - QDR.log.warn("unable to find nodeIndex for " + _id); - return -1; - } - - function getLink (_source, _target, dir, cls) { - for (var i=0; i < links.length; i++) { - var s = links[i].source, t = links[i].target; - if (typeof links[i].source == "object") { - s = s.id; - t = t.id; - } - if (s == _source && t == _target) { - return i; - } - // same link, just reversed - if (s == _target && t == _source) { - return -i; - } - } - - //QDR.log.debug("creating new link (" + (links.length) + ") between " + nodes[_source].name + " and " + nodes[_target].name); - var link = { - source: _source, - target: _target, - left: dir != "out", - right: dir == "out", - cls: cls - }; - return links.push(link) - 1; - } - - - function resetMouseVars() { - mousedown_node = null; - mouseup_node = null; - mousedown_link = null; - } - - // update force layout (called automatically each iteration) - function tick() { - // draw directed edges with proper padding from node centers - path.attr('d', function (d) { - //QDR.log.debug("in tick for d"); - //console.dump(d); - - var deltaX = d.target.x - d.source.x, - deltaY = d.target.y - d.source.y, - dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY), - normX = deltaX / dist, - normY = deltaY / dist; - var sourcePadding, targetPadding; - if (d.target.nodeType == "inter-router") { - // right arrow left line start - sourcePadding = d.left ? radius + 8 : radius; - // left arrow right line start - targetPadding = d.right ? radius + 16 : radius; - } else { - sourcePadding = d.left ? radiusNormal + 18 : radiusNormal; - targetPadding = d.right ? radiusNormal + 16 : radiusNormal; - } - var sourceX = d.source.x + (sourcePadding * normX), - sourceY = d.source.y + (sourcePadding * normY), - targetX = d.target.x - (targetPadding * normX), - targetY = d.target.y - (targetPadding * normY); - return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY; - }); - - circle.attr('transform', function (d) { - d.x = Math.max(d.x, radiusNormal * 2); - d.y = Math.max(d.y, radiusNormal * 2); - return 'translate(' + d.x + ',' + d.y + ')'; - }); - if (!animate) { - animate = true; - force.stop(); - } - } - - // highlight the paths between the selected node and the hovered node - function findNextHopNode(from, d) { - // d is the node that the mouse is over - // from is the selected_node .... - if (!from) - return null; - - if (from == d) - return selected_node; - - //QDR.log.debug("finding nextHop from: " + from.name + " to " + d.name); - var sInfo = QDRService.topology.nodeInfo()[from.key]; - - if (!sInfo) { - QDR.log.warn("unable to find topology node info for " + from.key); - return null; - } - - // find the hovered name in the selected name's .router.node results - if (!sInfo['.router.node']) - return null; - var aAr = sInfo['.router.node'].attributeNames; - var vAr = sInfo['.router.node'].results; - for (var hIdx=0; hIdx<vAr.length; ++hIdx) { - var addrT = QDRService.valFor(aAr, vAr[hIdx], "routerId" ); - if (addrT == d.name) { - //QDR.log.debug("found " + d.name + " at " + hIdx); - var nextHop = QDRService.valFor(aAr, vAr[hIdx], "nextHop"); - //QDR.log.debug("nextHop was " + nextHop); - return (nextHop == null) ? nodeFor(addrT) : nodeFor(nextHop); - } - } - return null; - } - - function nodeFor(name) { - for (var i=0; i<nodes.length; ++i) { - if (nodes[i].name == name) - return nodes[i]; - } - return null; - } - - function linkFor(source, target) { - for (var i=0; i<links.length; ++i) { - if ((links[i].source == source) && (links[i].target == target)) - return links[i]; - if ((links[i].source == target) && (links[i].target == source)) - return links[i]; - } - // the selected node was a client/broker - //QDR.log.debug("failed to find a link between "); - //console.dump(source); - //QDR.log.debug(" and "); - //console.dump(target); - return null; - } - - // takes the nodes and links array of objects and adds svg elements for everything that hasn't already - // been added - function restart(start) { - circle.call(force.drag); - //svg.classed('ctrl', true); - - // path (link) group - path = path.data(links); - - // update existing links - path.classed('selected', function(d) { return d === selected_link; }) - .classed('highlighted', function(d) { return d.highlighted; } ) - .classed('temp', function(d) { return d.cls == 'temp'; } ) - .attr('marker-start', function(d) { - var sel = d===selected_link ? '-selected' : ''; - return d.left ? 'url('+urlPrefix+'#start-arrow' + sel + ')' : ''; }) - .attr('marker-end', function(d) { - var sel = d===selected_link ? '-selected' : ''; - return d.right ? 'url('+urlPrefix+'#end-arrow' + sel +')' : ''; }) - - - // add new links. if links[] is longer than the existing paths, add a new path for each new element - path.enter().append('svg:path') - .attr('class', 'link') - .attr('marker-start', function(d) { - var sel = d===selected_link ? '-selected' : ''; - return d.left ? 'url('+urlPrefix+'#start-arrow' + sel + ')' : ''; }) - .attr('marker-end', function(d) { - var sel = d===selected_link ? '-selected' : ''; - return d.right ? 'url('+urlPrefix+'#end-arrow' + sel + ')' : ''; }) - .classed('temp', function(d) { return d.cls == 'temp'; } ) - .on('mouseover', function (d) { - if($scope.addingNode.step > 0) { - if (d.cls == 'temp') { - d3.select(this).classed('over', true); - } - return; - } - //QDR.log.debug("showing connections form"); - var resultIndex = 0; // the connection to use - var left = d.left ? d.target : d.source; - // right is the node that the arrow points to, left is the other node - var right = d.left ? d.source : d.target; - var onode = QDRService.topology.nodeInfo()[left.key]; - // loop through all the connections for left, and find the one for right - if (!onode || !onode['.connection']) - return; - // update the info dialog for the link the mouse is over - if (!selected_node && !selected_link) { - for (resultIndex=0; resultIndex < onode['.connection'].results.length; ++resultIndex) { - var conn = onode['.connection'].results[resultIndex]; - /// find the connection whose container is the right's name - var name = QDRService.valFor(onode['.connection'].attributeNames, conn, "container"); - if (name == right.name) { - break; - } - } - // did not find connection. this is a connection to a non-interrouter node - if (resultIndex === onode['.connection'].results.length) { - // use the non-interrouter node's connection info - left = d.target; - resultIndex = left.resultIndex; - } - updateConnForm(left, resultIndex); - } - - // select link - mousedown_link = d; - selected_link = mousedown_link; - //selected_node = null; - //mousedown_node = null; - restart(); - }) - .on('mouseout', function (d) { - if($scope.addingNode.step > 0) { - if (d.cls == 'temp') { - d3.select(this).classed('over', false); - } - return; - } - //QDR.log.debug("showing connections form"); - // select link - selected_link = null; - //selected_node = null; - //mousedown_node = null; - restart(); - }) - .on("contextmenu", function(d) { - $(document).click(); - d3.event.preventDefault(); - if (d.cls !== "temp") - return; - - mousedown_link = d; - d3.select('#link_context_menu') - .style('left', (mouseX + $(document).scrollLeft()) + "px") - .style('top', (mouseY + $(document).scrollTop()) + "px") - .style('display', 'block'); - }) - .on("dblclick", function (d) { - var pos = d3.mouse(this); - var diameter = 400; - var format = d3.format(",d"); - var pack = d3.layout.pack() - .size([diameter - 4, diameter - 4]) - .padding(3) - .value(function(d) { return d.size; }); - - var svg = d3.select("#crosssection").append("svg") - .attr("width", diameter) - .attr("height", diameter); - var svgg = svg.append("g") - .attr("transform", "translate(2,2)"); - - svg.on('click', function (d) { - d3.select("#crosssection").style("display","none"); - }) - - var root = { - name: "links between " + d.source.name + " and " + d.target.name, - children: [] - } - var nodeInfo = QDRService.topology.nodeInfo(); - var connections = nodeInfo[d.source.key]['.connection']; - var containerIndex = connections.attributeNames.indexOf('container'); - connections.results.some ( function (connection) { - if (connection[containerIndex] == d.target.containerName) { - root.attributeNames = connections.attributeNames; - root.obj = connection; - root.desc = "Connection"; - return true; // stop looping after 1 match - } - return false; - }) - - // find router.links where link.remoteContainer is d.source.name - var links = nodeInfo[d.source.key]['.router.link']; - containerIndex = links.attributeNames.indexOf('remoteContainer'); - var nameIndex = links.attributeNames.indexOf('name'); - var linkDirIndex = links.attributeNames.indexOf('linkDir'); - links.results.forEach ( function (link) { - if (link[containerIndex] == d.target.containerName) - root.children.push ( - { name: "(" + link[linkDirIndex] + ") " + link[nameIndex], - size: 100, - obj: link, - desc: "Link", - attributeNames: links.attributeNames - }) - }) - if (root.children.length == 0) - return; - var node = svgg.datum(root).selectAll(".node") - .data(pack.nodes) - .enter().append("g") - .attr("class", function(d) { return d.children ? "parent node hastip" : "leaf node hastip"; }) - .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) - .attr("title", function (d) { - var title = "<h4>" + d.desc + "</h4><table class='tiptable'><tbody>"; - if (d.attributeNames) - d.attributeNames.forEach( function (n, i) { - title += "<tr><td>" + n + "</td><td>"; - title += d.obj[i] != null ? d.obj[i] : ''; - title += '</td></tr>'; - }) - title += "</tbody></table>" - return title - }) - - node.append("circle") - .attr("r", function(d) { return d.r; }); - -// node.filter(function(d) { return !d.children; }).append("text") - node.append("text") - .attr("dy", function (d) { return d.children ? "-10em" : ".3em"}) - .style("text-anchor", "middle") - .text(function(d) { - return d.name.substring(0, d.r / 3); - }); - - $('.hastip').tooltipsy({ alignTo: 'cursor'}); - d3.select("#crosssection").style("display","block"); - }) - - - // remove old links - path.exit().remove(); - - - // circle (node) group - // nodes are known by id - circle = circle.data(nodes, function (d) { - return d.id; - }); - - // update existing nodes visual states - circle.selectAll('circle') - .classed('selected', function (d) { return (d === selected_node) }) - .classed('fixed', function (d) { return (d.fixed & 0b1) }) - - // add new circle nodes. if nodes[] is longer than the existing paths, add a new path for each new element - var g = circle.enter().append('svg:g'); - - // add new circles and set their attr/class/behavior - g.append('svg:circle') - .attr('class', 'node') - .attr('r', function (d) { - return radii[d.nodeType]; - }) - .classed('fixed', function (d) {return d.fixed}) - .classed('temp', function(d) { return QDRService.nameFromId(d.key) == '__internal__'; } ) - .classed('normal', function(d) { return d.nodeType == 'normal' } ) - .classed('inter-router', function(d) { return d.nodeType == 'inter-router' } ) - .classed('on-demand', function(d) { return d.nodeType == 'on-demand' } ) - -/* - .style('fill', function (d) { - var sColor = colors[d.nodeType]; - return (d === selected_node) ? d3.rgb(sColor).brighter().toString() : d3.rgb(sColor); - }) - .style('stroke', function (d) { - var sColor = colors[d.nodeType]; - return d3.rgb(sColor).darker().toString(); - }) -*/ - .on('mouseover', function (d) { - if ($scope.addingNode.step > 0) { - d3.select(this).attr('transform', 'scale(1.1)'); - return; - } - if (!selected_node) { - if (d.nodeType === 'inter-router') { - //QDR.log.debug("showing general form"); - updateNodeForm(d); - } else if (d.nodeType === 'normal' || d.nodeType === 'on-demand') { - //QDR.log.debug("showing connections form"); - updateConnForm(d, d.resultIndex); - } - } - - if (d === mousedown_node) - return; - //if (d === selected_node) - // return; - // enlarge target node - d3.select(this).attr('transform', 'scale(1.1)'); - // highlight the next-hop route from the selected node to this node - mousedown_node = null; - - if (!selected_node) { - return; - } - setTimeout(nextHop, 1, selected_node, d); - }) - .on('mouseout', function (d) { - // unenlarge target node - d3.select(this).attr('transform', ''); - for (var i=0; i<links.length; ++i) { - links[i]['highlighted'] = false; - } - restart(); - }) - .on('mousedown', function (d) { - if (d3.event.button !== 0) { // ignore all but left button - return; - } - mousedown_node = d; - // mouse position relative to svg - initial_mouse_down_position = d3.mouse(this.parentElement.parentElement.parentElement).slice(); - }) - .on('mouseup', function (d) { - if (!mousedown_node) - return; - - selected_link = null; - // unenlarge target node - d3.select(this).attr('transform', ''); - - // check for drag - mouseup_node = d; - var mySvg = this.parentElement.parentElement.parentElement; - // if we dragged the node, make it fixed - var cur_mouse = d3.mouse(mySvg); - if (cur_mouse[0] != initial_mouse_down_position[0] || - cur_mouse[1] != initial_mouse_down_position[1]) { - console.log("mouse pos changed. making this node fixed") - d3.select(this).classed("fixed", d.fixed = true); - resetMouseVars(); - return; - } - - // we didn't drag, we just clicked on the node - if ($scope.addingNode.step > 0) { - if (d.nodeType !== 'inter-router') - return; - if (QDRService.nameFromId(d.key) == '__internal__') - return; - - // add a link from the clicked node to the new node - getLink(d.id, nodes.length-1, "in", "temp"); - $scope.addingNode.hasLink = true; - $scope.$apply(); - // add new elements to the svg - force.links(links).start(); - restart(); - return; - - } - - // if this node was selected, unselect it - if (mousedown_node === selected_node) { - selected_node = null; - $scope.topoFormSelected = ""; - } - else { - selected_node = mousedown_node; - if (d.nodeType === 'inter-router') { - //QDR.log.debug("showing general form"); - updateNodeForm(d); - $scope.topoFormSelected = "general"; - } else if (d.nodeType === 'normal' || d.nodeType === 'on-demand') { - //QDR.log.debug("showing connections form"); - updateConnForm(d, d.resultIndex); - $scope.topoFormSelected = "connections"; - } - } - for (var i=0; i<links.length; ++i) { - links[i]['highlighted'] = false; - } - mousedown_node = null; - $scope.$apply(); - restart(false); - - }) - .on("dblclick", function (d) { - if (d.fixed) { - d3.select(this).classed("fixed", d.fixed = false); - force.start(); // let the nodes move to a new position - } - if (QDRService.nameFromId(d.key) == '__internal__') { - editNode(); - $scope.$apply(); - } - }) - .on("contextmenu", function(d) { - $(document).click(); - d3.event.preventDefault(); - $scope.contextNode = d; - $scope.$apply(); // we just changed a scope valiable during an async event - d3.select('#node_context_menu') - .style('left', (mouseX + $(document).scrollLeft()) + "px") - .style('top', (mouseY + $(document).scrollTop()) + "px") - .style('display', 'block'); - - }); - - // show node IDs - g.append('svg:text') - .attr('x', 0) - .attr('y', 4) - .attr('class', 'id') - .text(function (d) { - return (d.nodeType === 'normal' || d.nodeType == 'on-demand') ? d.name.slice(-1) : - d.name.length>7 ? d.name.substr(0,6)+'...' : d.name; - }); - - // remove old nodes - circle.exit().remove(); - - if (!mousedown_node || !selected_node) - return; - - if (!start) - return; - // set the graph in motion - //QDR.log.debug("mousedown_node is " + mousedown_node); - force.start(); - - } - - function nextHop(thisNode, d) { - if ((thisNode) && (thisNode != d)) { - var target = findNextHopNode(thisNode, d); - //QDR.log.debug("highlight link from node "); - //console.dump(nodeFor(selected_node.name)); - //console.dump(target); - if (target) { - var hlLink = linkFor(nodeFor(thisNode.name), target); - //QDR.log.debug("need to highlight"); - //console.dump(hlLink); - if (hlLink) - hlLink['highlighted'] = true; - else - target = null; - } - setTimeout(nextHop, 1, target, d); - } - restart(); - } - - - function mousedown() { - // prevent I-bar on drag - //d3.event.preventDefault(); - - // because :active only works in WebKit? - svg.classed('active', true); - } - - QDRService.addUpdatedAction("topology", function() { - //QDR.log.debug("Topology controller was notified that the model was updated"); - if (hasChanged()) { - QDR.log.info("svg graph changed") - saveChanged(); - // TODO: update graph nodes instead of rebuilding entire graph - d3.select("#SVG_ID").remove(); - animate = true; - initForceGraph(); - initGlobe(cities); - //if ($location.path().startsWith("/topology")) - // Core.notification('info', "Qpid dispatch router topology changed"); - - } else { - //QDR.log.debug("no changes") - } - }); - - function hasChanged () { - var nodeInfo = QDRService.topology.nodeInfo(); - if (Object.keys(nodeInfo).length != Object.keys(savedKeys).length) - return true; - for (var key in nodeInfo) { - // if this node isn't in the saved node list - if (!savedKeys.hasOwnProperty(key)) - return true; - // if the number of connections for this node chaanged - if (nodeInfo[key]['.connection'].results.length != savedKeys[key]) { - /* - QDR.log.debug("number of connections changed for " + key); - QDR.log.debug("QDRService.topology._nodeInfo[key]['.connection'].results.length"); - console.dump(QDRService.topology._nodeInfo[key]['.connection'].results.length); - QDR.log.debug("savedKeys[key]"); - console.dump(savedKeys[key]); - */ - return true; - } - } - return false; - }; - function saveChanged () { - savedKeys = {}; - var nodeInfo = QDRService.topology.nodeInfo(); - // save the number of connections per node - for (var key in nodeInfo) { - savedKeys[key] = nodeInfo[key]['.connection'].results.length; - } - //QDR.log.debug("saving current keys"); - console.dump(savedKeys); - }; - // we are about to leave the page, save the node positions - $rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) { - //QDR.log.debug("locationChangeStart"); - nodes.forEach( function (d) { - localStorage[d.name] = angular.toJson({x: d.x, y: d.y, fixed: d.fixed}); - }); - $scope.addingNode.step = 0; - - }); - // When the DOM element is removed from the page, - // AngularJS will trigger the $destroy event on - // the scope - $scope.$on("$destroy", function( event ) { - //QDR.log.debug("scope on destroy"); - QDRService.stopUpdating(); - QDRService.delUpdatedAction("topology"); - d3.select("#SVG_ID").remove(); - }); - - initForceGraph(); - saveChanged(); - QDRService.startUpdating(); - - initGlobe(cities)//, "Boston", "Tel Aviv-Yafo"]); - - function doAddDialog(NewRouterName) { - var modalInstance = $uibModal.open({ - animation: true, - controller: 'QDR.NodeDialogController', - templateUrl: 'node-config-template.html', - size: 'lg', - resolve: { - newname: function () { - return NewRouterName; - } - } - }); - modalInstance.result.then(function (result) { - if (result) - setTimeout(doDownloadDialog, 100, result); - }); - }; - - function doDownloadDialog(result) { - var modalInstance = $uibModal.open({ - animation: true, - controller: 'QDR.DownloadDialogController', - templateUrl: 'download-dialog-template.html', - resolve: { - results: function () { - return result; - } - } - }); - }; - }]); - - QDR.module.controller("QDR.NodeDialogController", function($scope, QDRService, $uibModalInstance, newname) { - var schema = QDRService.schema; - var myEntities = ['container', 'router', 'log', 'listener' ]; - var typeMap = {integer: 'number', string: 'text', path: 'text', boolean: 'boolean'}; - var newLinks = $('path.temp').toArray(); // jquery array of new links for the added router - var nodeInfo = QDRService.topology.nodeInfo(); - var separatedEntities = []; // additional entities required if a link is reversed - var myPort = 0, myAddr = '0.0.0.0'; // port and address for new router - $scope.entities = []; - - // find max port number that is used in all the listeners - var getMaxPort = function (nodeInfo) { - var maxPort = 5674; - for (var key in nodeInfo) { - var node = nodeInfo[key]; - var listeners = node['.listener']; - var attrs = listeners.attributeNames; - for (var i=0; i<listeners.results.length; ++i) { - var res = listeners.results[i]; - var port = QDRService.valFor(attrs, res, 'port'); - if (parseInt(port, 10) > maxPort) - maxPort = parseInt(port, 10); - } - } - return maxPort; - } - var maxPort = getMaxPort(nodeInfo); - - // construct an object that contains all the info needed for a single tab's fields - var entity = function (actualName, tabName, humanName, ent, icon, link) { - var nameIndex = -1; // the index into attributes that the name field was placed - var index = 0; - var info = { - actualName: actualName, - tabName: tabName, - humanName: humanName, - description:ent.description, - icon: angular.isDefined(icon) ? icon : '', - references: ent.references, - link: link, - - attributes: $.map(ent.attributes, function (value, key) { - // skip identity and depricated fields - if (key == 'identity' || value.description.startsWith('Deprecated')) - return null; - var val = value['default']; - if (key == 'name') - nameIndex = index; - index++; - return { name: key, - humanName: QDRService.humanify(key), - description:value.description, - type: typeMap[value.type], - rawtype: value.type, - input: typeof value.type == 'string' ? value.type == 'boolean' ? 'boolean' : 'input' - : 'select', - selected: val ? val : undefined, - 'default': value['default'], - value: val, - required: value.required, - unique: value.unique - }; - }) - } - // move the 'name' attribute to the 1st position - if (nameIndex > -1) { - var tmp = info.attributes[0]; - info.attributes[0] = info.attributes[nameIndex]; - info.attributes[nameIndex] = tmp; - } - return info; - } - - // remove the annotation fields - var stripAnnotations = function (entityName, ent, annotations) { - if (ent.references) { - var newEnt = {attributes: {}}; - ent.references.forEach( function (annoKey) { - if (!annotations[annoKey]) - annotations[annoKey] = {}; - annotations[annoKey][entityName] = true; // create the key/consolidate duplicates - var keys = Object.keys(schema.annotations[annoKey].attributes); - for (var attrib in ent.attributes) { - if (keys.indexOf(attrib) == -1) { - newEnt.attributes[attrib] = ent.attributes[attrib]; - } - } - // add a field for the reference name - newEnt.attributes[annoKey] = {type: 'string', - description: 'Name of the ' + annoKey + ' section.', - 'default': annoKey, required: true}; - }) - newEnt.references = ent.references; - newEnt.description = ent.description; - return newEnt; - } - return ent; - } - - var annotations = {}; - myEntities.forEach(function (entityName) { - var ent = schema.entityTypes[entityName]; - var hName = QDRService.humanify(entityName); - if (entityName == 'listener') - hName = "Listener for clients"; - var noAnnotations = stripAnnotations(entityName, ent, annotations); - var ediv = entity(entityName, entityName, hName, noAnnotations, undefined); - if (ediv.actualName == 'router') { - ediv.attributes.filter(function (attr) { return attr.name == 'name'})[0].value = newname; - // if we have any new links (connectors), then the router's mode should be interior - if (newLinks.length) { - var roleAttr = ediv.attributes.filter(function (attr) { return attr.name == 'mode'})[0]; - roleAttr.value = roleAttr.selected = "interior"; - } - } - if (ediv.actualName == 'container') { - ediv.attributes.filter(function (attr) { return attr.name == 'containerName'})[0].value = newname + "-container"; - } - if (ediv.actualName == 'listener') { - // find max port number that is used in all the listeners - ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = ++maxPort; - } - // special case for required log.module since it doesn't have a default - if (ediv.actualName == 'log') { - var moduleAttr = ediv.attributes.filter(function (attr) { return attr.name == 'module'})[0]; - moduleAttr.value = moduleAttr.selected = "DEFAULT"; - } - $scope.entities.push( ediv ); - }) - - // add a tab for each annotation that was found - var annotationEnts = []; - for (var key in annotations) { - ent = angular.copy(schema.annotations[key]); - ent.attributes.name = {type: "string", unique: true, description: "Unique name that is used to refer to this set of attributes."} - var ediv = entity(key, key+'tab', QDRService.humanify(key), ent, undefined); - ediv.attributes.filter(function (attr) { return attr.name == 'name'})[0].value = key; - $scope.entities.push( ediv ); - annotationEnts.push( ediv ); - } - - // add an additional listener tab if any links are reversed - ent = schema.entityTypes['listener']; - newLinks.some(function (link) { - if (link.__data__.right) { - var noAnnotations = stripAnnotations('listener', ent, annotations); - var ediv = entity("listener", "listener0", "Listener (internal)", noAnnotations, undefined); - ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = ++maxPort; - // connectors from other routers need to connect to this addr:port - myPort = maxPort; - myAddr = ediv.attributes.filter(function (attr) { return attr.name == 'addr'})[0].value - - // override the role. 'normal' is the default, but we want inter-router - ediv.attributes.filter(function( attr ) { return attr.name == 'role'})[0].selected = 'inter-router'; - separatedEntities.push( ediv ); - return true; // stop looping - } - return false; // continue looping - }) - - // Add connector tabs for each new link on the topology graph - ent = schema.entityTypes['connector']; - newLinks.forEach(function (link, i) { - var noAnnotations = stripAnnotations('connector', ent, annotations); - var ediv = entity('connector', 'connector' + i, " " + link.__data__.source.name, noAnnotations, link.__data__.right, link) - - // override the connector role. 'normal' is the default, but we want inter-router - ediv.attributes.filter(function( attr ) { return attr.name == 'role'})[0].selected = 'inter-router'; - - // find the addr:port of the inter-router listener to use - var listener = nodeInfo[link.__data__.source.key]['.listener']; - var attrs = listener.attributeNames; - for (var i=0; i<listener.results.length; ++i) { - var res = listener.results[i]; - var role = QDRService.valFor(attrs, res, 'role'); - if (role == 'inter-router') { - ediv.attributes.filter(function( attr ) { return attr.name == 'addr'})[0].value = - QDRService.valFor(attrs, res, 'addr') - ediv.attributes.filter(function( attr ) { return attr.name == 'port'})[0].value = - QDRService.valFor(attrs, res, 'port') - break; - } - } - if (link.__data__.right) { - // connectors from other nodes need to connect to the new router's listener addr:port - ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = myPort; - ediv.attributes.filter(function (attr) { return attr.name == 'addr'})[0].value = myAddr; - - separatedEntities.push(ediv) - } - else - $scope.entities.push( ediv ); - }) - Array.prototype.push.apply($scope.entities, separatedEntities); - - // update the description on all the annotation tabs - annotationEnts.forEach ( function (ent) { - var shared = Object.keys(annotations[ent.actualName]); - ent.description += " These fields are shared by " + shared.join(" and ") + "."; - - }) - - $scope.cancel = function () { - $uibModalInstance.close() - }; - $scope.testPattern = function (attr) { - if (attr.rawtype == 'path') - return /^(\/)?([^/\0]+(\/)?)+$/; - //return /^(.*\/)([^/]*)$/; - return /(.*?)/; - } - - $scope.attributeDescription = ''; - $scope.attributeType = ''; - $scope.attributeRequired = ''; - $scope.attributeUnique = ''; - $scope.active = 'container' - $scope.fieldsetDivs = "/fieldsetDivs.html" - $scope.setActive = function (tabName) { - $scope.active = tabName - } - $scope.isActive = function (tabName) { - return $scope.active === tabName - } - $scope.showDescription = function (attr, e) { - $scope.attributeDescription = attr.description; - var offset = jQuery(e.currentTarget).offset() - jQuery('.attr-description').offset({top: offset.top}) - - $scope.attributeType = "Type: " + JSON.stringify(attr.rawtype); - $scope.attributeRequired = attr.required ? 'required' : ''; - $scope.attributeUnique = attr.unique ? 'Must be unique' : ''; - } - // handle the download button click - // copy the dialog's values to the original node - $scope.download = function () { - $uibModalInstance.close({entities: $scope.entities, annotations: annotations}); - } - - $scope.selectAnnotationTab = function (tabName) { - var tabs = $( "#tabs" ).tabs(); - tabs.tabs("select", tabName); - } - - var initTabs = function () { - var div = angular.element("#tabs"); - if (!div.width()) { - setTimeout(initTabs, 100); - return; - } - $( "#tabs" ) - .tabs() - .addClass('ui-tabs-vertical ui-helper-clearfix'); - } - // start the update loop - initTabs(); - - }); - -QDR.module.controller("QDR.DownloadDialogController", function($scope, QDRService, $templateCache, $window, $uibModalInstance, results) { - - var result = results.entities; - var annotations = results.annotations; - var annotationKeys = Object.keys(annotations); - var annotationSections = {}; - - // use the router's name as the file name if present - $scope.newRouterName = 'router'; - result.forEach( function (e) { - if (e.actualName == 'router') { - e.attributes.forEach( function (a) { - if (a.name == 'name') { - $scope.newRouterName = a.value; - } - }) - } - }) - $scope.newRouterName = $scope.newRouterName + ".conf"; - - var template = $templateCache.get('config-file-header.html'); - $scope.verbose = true; - $scope.$watch('verbose', function (newVal) { - if (newVal !== undefined) { - // recreate output using current verbose setting - getOutput(); - } - }) - - var getOutput = function () { - $scope.output = template + '\n'; - $scope.parts = []; - var commentChar = '#' - result.forEach(function (entity) { - // don't output a section for annotations, they get flattened into the entities - var section = ""; - if (entity.icon) { - section += "##\n## Add to " + entity.link.__data__.source.name + "'s configuration file\n##\n"; - } - section += "##\n## " + QDRService.humanify(entity.actualName) + " - " + entity.description + "\n##\n"; - section += entity.actualName + " {\n"; - entity.attributes.forEach(function (attribute) { - if (attribute.input == 'select') - attribute.value = attribute.selected; - - // treat values with all spaces and empty strings as undefined - attribute.value = String(attribute.value).trim(); - if (attribute.value === 'undefined' || attribute.value === '') - attribute.value = undefined; - - if ($scope.verbose) { - commentChar = attribute.required || attribute.value != attribute['default'] ? ' ' : '#'; - if (!attribute.value) { - commentChar = '#'; - attribute.value = ''; - } - section += commentChar + " " - + attribute.name + ":" + Array(Math.max(20 - attribute.name.length, 1)).join(" ") - + attribute.value - + Array(Math.max(20 - ((attribute.value)+"").length, 1)).join(" ") - + '# ' + attribute.description - + "\n"; - } else { - if (attribute.value) { - if (attribute.value != attribute['default'] || attribute.required) - section += " " - + attribute.name + ":" + Array(20 - attribute.name.length).join(" ") - + attribute.value + "\n"; - - } - } - }) - section += "}\n\n"; - // if entity.icon is true, this is a connector intended for another router - if (entity.icon) - $scope.parts.push({output: section, - link: entity.link, - name: entity.link.__data__.source.name, - references: entity.references}); - else - $scope.output += section; - - // if this section is actually an annotation - if (annotationKeys.indexOf(entity.actualName) > -1) { - annotationSections[entity.actualName] = section; - } - }) - // go back and add annotation sections to the parts - $scope.parts.forEach (function (part) { - for (var section in annotationSections) { - if (part.references.indexOf(section) > -1) { - part.output += annotationSections[section]; - } - } - }) - QDR.log.debug($scope.output); - } - - // handle the download button click - $scope.download = function () { - var blob = new Blob([$scope.output], { type: 'text/plain' }); - var downloadLink = angular.element('<a></a>'); - downloadLink.attr('href', ($window.URL || $window.webkitURL).createObjectURL(blob)); - downloadLink.attr('download', $scope.newRouterName); - downloadLink[0].click(); - } - - $scope.downloadPart = function (part) { - var linkName = part.link.__data__.source.name + 'additional.conf'; - var blob = new Blob([part.output], { type: 'text/plain' }); - var downloadLink = angular.element('<a></a>'); - downloadLink.attr('href', ($window.URL || $window.webkitURL).createObjectURL(blob)); - downloadLink.attr('download', linkName); - downloadLink[0].click(); - - QDR.log.debug(part); - } - - $scope.done = function () { - $uibModalInstance.close(); - } -}); - - return QDR; -}(QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/plugin/lib/jquery.tipsy.js ---------------------------------------------------------------------- diff --git a/console/plugin/lib/jquery.tipsy.js b/console/plugin/lib/jquery.tipsy.js deleted file mode 100644 index 7c12d4e..0000000 --- a/console/plugin/lib/jquery.tipsy.js +++ /dev/null @@ -1,418 +0,0 @@ -// tipsy, facebook style tooltips for jquery -// version 1.0.2 -// (c) 2008-2010 jason frame [[email protected]] -// released under the MIT license - -(function($, window, undefined) { - - function maybeCall(thing, ctx) { - return (typeof thing == 'function') ? (thing.call(ctx)) : thing; - } - - function isElementInDOM(ele) { - while (ele = ele.parentNode) { - if (ele == document) return true; - } - return false; - } - - // Returns true if it is a DOM element - // http://stackoverflow.com/a/384380/999 - function isElement(o){ - return ( - typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 - o && typeof o === "object" && o.nodeType === 1 && typeof o.nodeName==="string" - ); - } - - var tipsyIDcounter = 0; - function tipsyID() { - return "tipsyuid" + (tipsyIDcounter++); - } - - function Tipsy(element, options) { - this.$element = $(element); - this.options = options; - this.enabled = true; - this.fixTitle(); - } - - Tipsy.prototype = { - show: function() { - if (!isElementInDOM(this.$element[0])) { - return; - } - - if (isElement(this.$element) && !this.$element.is(':visible')) { - return; - } - - var title; - if (this.enabled && (title = this.getTitle())) { - var $tip = this.tip(); - - $tip.find('.tipsy-inner' + this.options.theme)[this.options.html ? 'html' : 'text'](title); - - $tip[0].className = 'tipsy' + this.options.theme; // reset classname in case of dynamic gravity - if (this.options.className) { - $tip.addClass(maybeCall(this.options.className, this.$element[0])); - } - - $tip.remove().css({top: 0, left: 0, width: 'auto', visibility: 'hidden', display: 'block'}).prependTo(document.body); - - var pos = $.extend({}, this.$element.offset()); - - // If the element is contained in a SVG object, use getBBox - if (this.$element.parents('svg').size() > 0) { - pos = $.extend(pos, this.$element[0].getBBox()); - } else { - pos = $.extend(pos, { - width: this.$element[0].offsetWidth || 0, - height: this.$element[0].offsetHeight || 0 - }); - } - - var actualWidth = $tip[0].offsetWidth, - actualHeight = $tip[0].offsetHeight, - gravity = maybeCall(this.options.gravity, this.$element[0]); - - var tp; - switch (gravity.charAt(0)) { - case 'n': - tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; - break; - case 's': - tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2}; - break; - case 'e': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset}; - break; - case 'w': - tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset}; - break; - default: - break; - } - - if (gravity.length == 2) { - if (gravity.charAt(1) == 'w') { - tp.left = pos.left + pos.width / 2 - 15; - } else { - tp.left = pos.left + pos.width / 2 - actualWidth + 15; - } - } - - $tip.css(tp).addClass('tipsy-' + gravity + this.options.theme); - $tip.find('.tipsy-arrow' + this.options.theme)[0].className = 'tipsy-arrow' + this.options.theme + ' tipsy-arrow-' + gravity.charAt(0) + this.options.theme; - $tip.css({width: (actualWidth - 8) + 'px'}); - - if (this.options.fade) { - if(this.options.shadow) - $(".tipsy-inner").css({'box-shadow': '0px 0px '+this.options.shadowBlur+'px '+this.options.shadowSpread+'px rgba(0, 0, 0, '+this.options.shadowOpacity+')', '-webkit-box-shadow': '0px 0px '+this.options.shadowBlur+'px '+this.options.shadowSpread+'px rgba(0, 0, 0, '+this.options.shadowOpacity+')'}); - $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity}, this.options.fadeInTime); - } else { - $tip.css({visibility: 'visible', opacity: this.options.opacity}); - } - - if (this.options.aria) { - var $tipID = tipsyID(); - $tip.attr("id", $tipID); - this.$element.attr("aria-describedby", $tipID); - } - } - }, - - hide: function() { - if (this.options.fade) { - this.tip().stop().fadeOut(this.options.fadeOutTime, function() { $(this).remove(); }); - } else { - this.tip().remove(); - } - if (this.options.aria) { - this.$element.removeAttr("aria-describedby"); - } - }, - - fixTitle: function() { - var $e = this.$element, - id = maybeCall(this.options.id, this.$element[0]); - if ($e.prop('title') || typeof($e.prop('original-title')) != 'string') { - $e.prop('original-title', $e.prop('title') || '').removeAttr('title'); - // add aria-describedby pointing to the tooltip's id - $e.attr('aria-describedby', id); - // if it doesn't already have a tabindex, force the trigger element into the tab cycle - // to make it keyboard accessible with tabindex=0. this automatically makes elements - // that are not normally keyboard accessible (div or span) that have been tipsy-fied - // also operable with the keyboard. - if ($e.attr('tabindex') === undefined) { - $e.attr('tabindex', 0); - } - } - }, - - getTitle: function() { - var title, $e = this.$element, o = this.options; - this.fixTitle(); - if (typeof o.title == 'string') { - title = $e.prop(o.title == 'title' ? 'original-title' : o.title); - } else if (typeof o.title == 'function') { - title = o.title.call($e[0]); - } - title = ('' + title).replace(/(^\s*|\s*$)/, ""); - return title || o.fallback; - }, - - tip: function() { - var id = maybeCall(this.options.id, this.$element[0]); - - if (!this.$tip) { - // generate tooltip, with appropriate ARIA role and an 'id' (can be set in options), - // so it can be targetted by aria-describedby in the trigger element - this.$tip = $('<div class="tipsy' + this.options.theme + '" id="'+id+'" role="tooltip"></div>').html('<div class="tipsy-arrow' + this.options.theme + '"></div><div class="tipsy-inner' + this.options.theme + '"></div>').attr("role","tooltip"); - this.$tip.data('tipsy-pointee', this.$element[0]); - } - return this.$tip; - }, - - validate: function() { - if (!this.$element[0].parentNode) { - this.hide(); - this.$element = null; - this.options = null; - } - }, - - enable: function() { this.enabled = true; }, - disable: function() { this.enabled = false; }, - toggleEnabled: function() { this.enabled = !this.enabled; } - }; - - $.fn.tipsy = function(options) { - - $.fn.tipsy.enable(); - - if (options === true) { - return this.data('tipsy'); - } else if (typeof options == 'string') { - var tipsy = this.data('tipsy'); - if (tipsy) tipsy[options](); - return this; - } - - options = $.extend({}, $.fn.tipsy.defaults, options); - - // Establish theme - options.theme = (options.theme && options.theme !== '') ? '-' + options.theme : ''; - - function get(ele) { - var tipsy = $.data(ele, 'tipsy'); - if (!tipsy) { - tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options)); - $.data(ele, 'tipsy', tipsy); - } - return tipsy; - } - - function enter() { - if ($.fn.tipsy.enabled !== true) { - return; - } - var tipsy = get(this); - tipsy.hoverState = 'in'; - if (options.delayIn === 0) { - tipsy.show(); - } else { - tipsy.fixTitle(); - setTimeout(function() { - if (tipsy.hoverState == 'in' && isElementInDOM(tipsy.$element)) { - tipsy.show(); - } - }, options.delayIn); - } - } - - function leave() { - var tipsy = get(this); - tipsy.hoverState = 'out'; - if (options.delayOut === 0) { - tipsy.hide(); - } else { - setTimeout(function() { if (tipsy.hoverState == 'out' || !tipsy.$element || !tipsy.$element.is(':visible')) tipsy.hide(); }, options.delayOut); - } - } - - if (!options.live) this.each(function() { get(this); }); - - if (options.trigger != 'manual') { - // one of the biggest changes from 1.0.0a: by default, bind to BOTH mouseenter/mouseleave AND focus/blur - // this ensures out-of-the-box keyboard accessibility, showing tooltips when users TAB to a (focusable) element - // sites that still use 'hover' will also get this new interactive behavior automatically, only those who - // explicitly set 'focus' will only get focus/blur (for forms, for instance) - - if (options.live && options.live !== true) { - if (options.trigger != 'focus') { - $(this).on('mouseenter', options.live, enter); - $(this).on('mouseleave', options.live, leave); - } - if (options.trigger != 'blur') { - $(this).on('focus', options.live, enter); - $(this).on('blur', options.live, leave); - } - } else { - if (options.live && !$.live) { - //live === true and using jQuery >= 1.9 - throw "Since jQuery 1.9, pass selector as live argument. eg. $(document).tipsy({live: 'a.live'});"; - } - var binder = options.live ? 'live' : 'bind'; - if (options.trigger != 'focus') { - this[binder]('mouseenter', enter)[binder]('mouseleave', leave); - } - if (options.trigger != 'blur') { - this[binder]('focus', enter)[binder]('blur', leave); - } - } - } - - return this; - - }; - - $.fn.tipsy.defaults = { - aria: false, - className: null, - id: 'tipsy', - delayIn: 0, - delayOut: 0, - fade: false, - fadeInTime: 400, - fadeOutTime: 400, - shadow: false, - shadowBlur: 8, - shadowOpacity: 1, - shadowSpread: 0, - fallback: '', - gravity: 'n', - html: false, - live: false, - offset: 0, - opacity: 0.8, - title: 'title', - trigger: 'interactive', - theme: '' - }; - - $.fn.tipsy.revalidate = function() { - $('.tipsy').each(function() { - var pointee = $.data(this, 'tipsy-pointee'); - if (!pointee || !isElementInDOM(pointee)) { - $(this).remove(); - } - }); - }; - - $.fn.tipsy.enable = function() { - $.fn.tipsy.enabled = true; - }; - - $.fn.tipsy.disable = function() { - $.fn.tipsy.enabled = false; - }; - - // Overwrite this method to provide options on a per-element basis. - // For example, you could store the gravity in a 'tipsy-gravity' attribute: - // return $.extend({}, options, {gravity: $(ele).prop('tipsy-gravity') || 'n' }); - // (remember - do not modify 'options' in place!) - $.fn.tipsy.elementOptions = function(ele, options) { - return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; - }; - - $.fn.tipsy.autoNS = function() { - return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; - }; - - $.fn.tipsy.autoWE = function() { - return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; - }; - - $.fn.tipsy.autoNWNE = function() { - return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'ne' : 'nw'; - }; - - $.fn.tipsy.autoSWSE = function() { - return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'se' : 'sw'; - }; - - /** - * yields a closure of the supplied parameters, producing a function that takes - * no arguments and is suitable for use as an autogravity function like so: - * - * @param marginNorth (int) - distance from the viewable region top edge that an - * element should be before setting its tooltip's gravity to be away - * from that edge. - * @param marginEast (int) - distance from the viewable region right edge that an - * element should be before setting its tooltip's gravity to be away - * from that edge. - * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer - * if there are no viewable region edges effecting the tooltip's - * gravity. It will try to vary from this minimally, for example, - * if 'sw' is preferred and an element is near the right viewable - * region edge, but not the top edge, it will set the gravity for - * that element's tooltip to be 'se', preserving the southern - * component. - */ - $.fn.tipsy.autoBounds = function(marginNorth, marginEast, prefer) { - return function() { - var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)}, - boundTop = $(document).scrollTop() + marginNorth, - boundLeft = $(document).scrollLeft() + marginEast, - $this = $(this); - - if ($this.offset().top < boundTop) dir.ns = 'n'; - if ($this.offset().left < boundLeft) dir.ew = 'w'; - if ($(window).width() + $(document).scrollLeft() - $this.offset().left < marginEast) dir.ew = 'e'; - if ($(window).height() + $(document).scrollTop() - $this.offset().top < marginNorth) dir.ns = 's'; - - return dir.ns + (dir.ew ? dir.ew : ''); - }; - }; - - /** - * Improved version of autoBounds for automatic placement of chunky tips - * The original autoBounds failed in two regards: 1. it would never return a 'w' or 'e', gravity even if they - * were preferred and/or optimal, 2. it only respected the margin between the left hand side of an element and - * left hand side of the viewport, and the top of an element and the top of the viewport. This version checks - * to see if the bottom of an element is too close to the bottom of the screen, similarly for the right hand side - */ - $.fn.tipsy.autoBounds2 = function(margin, prefer) { - return function() { - var dir = {}, - boundTop = $(document).scrollTop() + margin, - boundLeft = $(document).scrollLeft() + margin, - $this = $(this); - - // bi-directional string (ne, se, sw, etc...) - if (prefer.length > 1) { - dir.ns = prefer[0]; - dir.ew = prefer[1]; - } else { - // single direction string (e, w, n or s) - if (prefer[0] == 'e' || prefer[0] == 'w') { - dir.ew = prefer[0]; - } else { - dir.ns = prefer[0]; - } - } - - if ($this.offset().top < boundTop) dir.ns = 'n'; - if ($this.offset().left < boundLeft) dir.ew = 'w'; - if ($(window).width() + $(document).scrollLeft() - ($this.offset().left + $this.width()) < margin) dir.ew = 'e'; - if ($(window).height() + $(document).scrollTop() - ($this.offset().top + $this.height()) < margin) dir.ns = 's'; - - if (dir.ns) { - return dir.ns + (dir.ew ? dir.ew : ''); - } - return dir.ew; - } - }; - -})(jQuery, window); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
