Commit c4eebe0e by Shwetha GS

ATLAS-406 Resizing lineage window – should be an anchor on a corner – like ppt…

ATLAS-406 Resizing lineage window – should be an anchor on a corner – like ppt for graphic (sanjayp via shwethags)
parent 0e81ceb4
{
"name": "atlas-metadata",
"description": "Apache Atlas",
"version": "1.0.0-SNAPSHOT",
"devDependencies": {
"angular": "~1.2.15",
"angular-resource": "~1.2.15",
"angular-cookies": "~1.2.15",
"angular-route": "~1.2.15",
"angular-sanitize": "~1.2.15",
"bootstrap": "~3.1.1",
"angular-bootstrap": "~0.12.0",
"angular-ui-router": "~0.2.13",
"d3": "~3.5.3",
"d3-tip": "~0.6.6",
"lodash": "~3.0.0",
"angular-ui-utils": "~0.1.1",
"font-awesome": "~4.2.0",
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
},
"resolutions": {
"d3": "~3.5.3"
}
"name": "atlas-metadata",
"description": "Apache Atlas",
"version": "1.0.0-SNAPSHOT",
"devDependencies": {
"angular": "~1.2.15",
"angular-resource": "~1.2.15",
"angular-cookies": "~1.2.15",
"angular-route": "~1.2.15",
"angular-sanitize": "~1.2.15",
"bootstrap": "~3.1.1",
"angular-bootstrap": "~0.12.0",
"angular-ui-router": "~0.2.13",
"d3": "~3.5.3",
"d3-tip": "~0.6.6",
"lodash": "~3.0.0",
"angular-ui-utils": "~0.1.1",
"font-awesome": "~4.2.0",
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip",
"jquery-ui": "1.10.4"
},
"resolutions": {
"d3": "~3.5.3"
}
}
......@@ -154,9 +154,7 @@ module.exports = function(grunt) {
'hosts': [{
'hostnames': ['*'],
'routes': {
'/': distPath,
//'/api': 'http://162.249.6.39:21000/api'
'/api': 'http://ec2-52-25-142-7.us-west-2.compute.amazonaws.com:21000/api'
'/': distPath
}
}]
}
......
......@@ -114,14 +114,14 @@ header ul.menuBar li a{
header ul.menuBar li a>i {
margin-right: 3px;
font-size: 12px;
}
}
header ul.menuBar li a:hover{
color: #fff;
background: transparent;
}
}
header ul.menuBar li a:focus{
background: transparent;
}
}
header ul.menuBar li.active a{
color: #333333;
background-color: #ffffff;
......@@ -319,11 +319,11 @@ Tags on Home Page design
-o-text-overflow: ellipsis;
white-space: nowrap;
text-transform: capitalize;
float: left;
float: left;
background: lightblue !important;
color: black !important;
margin: 4px !important;
margin: 4px !important;
padding-left: 7px !important;
padding-right: 7px !important;
}
......@@ -335,7 +335,7 @@ Tags on Home Page design
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
white-space: nowrap;
text-transform: capitalize;
text-transform: capitalize;
}
.searchResultCount {
max-width: 500px;
......@@ -343,7 +343,7 @@ Tags on Home Page design
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
text-transform: capitalize;
text-transform: capitalize;
}
.maxwidth125px {
......@@ -361,11 +361,18 @@ Tags on Home Page design
.tagAlign{
text-align: center;
}
}
.h160 {
height: 160px !important;
}
.tag-attr{
font-weight:bold;
}
.zoom-buttons{
width:63px !important;
}
.graph{
overflow: hidden!important;
}
\ No newline at end of file
......@@ -69,7 +69,7 @@ div.lineage {
.lineage-viz {
margin: 0 auto;
overflow: auto;
overflow: hidden;
/*border: 1px solid #ddd;
border-top: none;*/
......@@ -78,6 +78,10 @@ div.lineage {
margin: 5px;
border-radius: 2px
}
.title-font{
font-size:12px!important;
}
/*.images {*/
/*background-image: url("../img/process.png");*/
/*}​*/
......
......@@ -36,6 +36,7 @@
<link rel="stylesheet" href="/css/lineage.css">
<link rel="stylesheet" href="/css/tags.css">
<link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="/lib/jquery-ui/themes/ui-lightness/jquery-ui.css">
</head>
......@@ -68,6 +69,7 @@
<script src="/lib/lodash/lodash.js"></script>
<script src="/lib/d3/d3.js"></script>
<script src="/lib/d3-tip/index.js"></script>
<script src="/lib/jquery-ui/ui/jquery-ui.js"></script>
<script src="/js/app.min.js"></script>
</body>
......
/*
* 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.
*/
'use strict';
angular.module('dgc.lineage').controller('LineageController', ['$element', '$scope', '$state', '$stateParams', 'lodash', 'LineageResource', 'd3', 'DetailsResource', '$q',
function($element, $scope, $state, $stateParams, _, LineageResource, d3, DetailsResource, $q) {
var guidsList = [];
function getLineageData(tableData, callRender) {
LineageResource.get({
tableName: tableData.tableName,
type: tableData.type
}, function lineageSuccess(response) {
if (!_.isEmpty(response.results.values.vertices)) {
var allGuids = loadProcess(response.results.values.edges, response.results.values.vertices);
allGuids.then(function(res) {
guidsList = res;
$scope.lineageData = transformData(response.results);
if (callRender) {
render();
}
})
.on("dblclick", function(d) {
$state.go("details", {
id: d.guid
});
});
}else{
$scope.requested = false;
}
});
}
function loadProcess(edges, vertices) {
var urlCalls = [];
var deferred = $q.defer();
for (var guid in edges) {
if (!vertices.hasOwnProperty(guid)) {
urlCalls.push(DetailsResource.get({
id: guid
}).$promise);
}
}
$q.all(urlCalls)
.then(function(results) {
deferred.resolve(results);
});
return deferred.promise;
}
$scope.type = $element.parent().attr('data-table-type');
$scope.requested = false;
$scope.height = $element[0].offsetHeight;
$scope.width = $element[0].offsetWidth;
function render() {
renderGraph($scope.lineageData, {
eleObj : $element,
element: $element[0],
height: $scope.height,
width: $scope.width
});
$scope.rendered = true;
}
$scope.onReset = function(){
renderGraph($scope.lineageData, {
eleObj : $element,
element: $element[0],
height: $scope.height,
width: $scope.width
});
};
$scope.$on('render-lineage', function(event, lineageData) {
if (lineageData.type === $scope.type) {
if (!$scope.lineageData) {
if (!$scope.requested) {
getLineageData(lineageData, true);
$scope.requested = true;
}
} else {
render();
}
}
});
function transformData(metaData) {
var edges = metaData.values.edges,
vertices = metaData.values.vertices,
nodes = {};
function getNode(guid) {
var name, type, tip;
if (vertices.hasOwnProperty(guid)) {
name = vertices[guid].values.name;
type = vertices[guid].values.vertexId.values.typeName;
} else {
var loadProcess = getLoadProcessTypes(guid);
if (typeof loadProcess !== "undefined") {
name = loadProcess.name;
type = loadProcess.typeName;
tip = loadProcess.tip;
} else {
name = 'Load Process';
type = 'Load Process';
}
}
var vertex = {
guid: guid,
name: name,
type: type,
tip: tip
};
if (!nodes.hasOwnProperty(guid)) {
nodes[guid] = vertex;
}
return nodes[guid];
}
function getLoadProcessTypes(guid) {
var procesRes = [];
angular.forEach(guidsList, function(value) {
if (value.id.id === guid) {
procesRes.name = value.values.name;
procesRes.typeName = value.typeName;
procesRes.tip = value.values.queryText;
}
});
return procesRes;
}
function attachParent(edge, node) {
edge.forEach(function eachPoint(childGuid) {
var childNode = getNode(childGuid);
node.children = node.children || [];
node.children.push(childNode);
childNode.parent = node.guid;
});
}
/* Loop through all edges and attach them to correct parent */
for (var guid in edges) {
var edge = edges[guid],
node = getNode(guid);
/* Attach parent to each endpoint of edge */
attachParent(edge, node);
}
/* Return the first node w/o parent, this is root node*/
return _.find(nodes, function(node) {
return !node.hasOwnProperty('parent');
});
}
function renderGraph(data, container) {
// ************** Generate the tree diagram *****************
var element = d3.select(container.element),
widthg = Math.max(container.width, 960),
heightg = Math.max(container.height, 500),
totalNodes = 0,
maxLabelLength = 0,
selectedNode = null,
draggingNode = null,
dragListener = null,
dragStarted = true,
domNode = null,
multiParents = null,
nodes = null,
tooltip = null,
node = null,
i = 0,
duration = 750,
root,
depthwidth = 10;
var viewerWidth = widthg - 15,
viewerHeight = heightg;
var tree = d3.layout.tree().nodeSize([100, 200]);
/*.size([viewerHeight, viewerWidth]);*/
container.eleObj.find(".graph").html('');
container.eleObj.find("svg").remove();
// define a d3 diagonal projection for use by the node paths later on.
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
// A recursive helper function for performing some setup by walking through all nodes
function visit(parent, visitFn, childrenFn) {
if (!parent) return;
visitFn(parent);
var children = childrenFn(parent);
if (children) {
var count = children.length;
for (var i = 0; i < count; i++) {
visit(children[i], visitFn, childrenFn);
}
}
}
// Call visit function to establish maxLabelLength
visit(data, function(d) {
totalNodes++;
maxLabelLength = Math.max(d.name.length, maxLabelLength);
}, function(d) {
return d.children && d.children.length > 0 ? d.children : null;
});
// sort the tree according to the node names
function sortTree() {
tree.sort(function(a, b) {
return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
});
}
// Sort the tree initially incase the JSON isn't in a sorted order.
sortTree();
// Define the zoom function for the zoomable tree
function zoom() {
svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
// define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);
/* Initialize tooltip */
tooltip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d) {
return '<pre class="alert alert-success">' + d.name + '</pre>';
});
// define the baseSvg, attaching a class for styling and the zoomListener
var baseSvg = element.append('svg')
.attr("width", viewerWidth)
.attr("height", viewerHeight)
.attr("class", "overlay")
.call(zoomListener)
.call(tooltip);
// Define the drag listeners for drag/drop behaviour of nodes.
dragListener = d3.behavior.drag()
.on("dragstart", function(d) {
if (d ===root) {
return;
}
dragStarted = true;
nodes = tree.nodes(d);
d3.event.sourceEvent.stopPropagation();
// it's important that we suppress the mouseover event on the node being dragged. Otherwise it will absorb the mouseover event and the underlying node will not detect it d3.select(this).attr('pointer-events', 'none');
})
.on("dragend", function(d) {
if (d ===root) {
return;
}
domNode = this;
if (selectedNode) {
// now remove the element from the parent, and insert it into the new elements children
var index = draggingNode.parent.children.indexOf(draggingNode);
if (index > -1) {
draggingNode.parent.children.splice(index, 1);
}
if (typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') {
if (typeof selectedNode.children !== 'undefined') {
selectedNode.children.push(draggingNode);
} else {
selectedNode._children.push(draggingNode);
}
} else {
selectedNode.children = [];
selectedNode.children.push(draggingNode);
}
// Make sure that the node being added to is expanded so user can see added node is correctly moved
expand(selectedNode);
sortTree();
endDrag();
} else {
endDrag();
}
});
function endDrag() {
selectedNode = null;
d3.selectAll('.ghostCircle').attr('class', 'ghostCircle');
d3.select(domNode).attr('class', 'node');
// now restore the mouseover event or we won't be able to drag a 2nd time
d3.select(domNode).select('.ghostCircle').attr('pointer-events', '');
updateTempConnector();
if (draggingNode !== null) {
update(root);
centerNode(draggingNode);
draggingNode = null;
}
}
function expand(d) {
if (d._children) {
d.children = d._children;
d.children.forEach(expand);
d._children = null;
}
}
// Function to update the temporary connector indicating dragging affiliation
var updateTempConnector = function() {
var data = [];
if (draggingNode !== null && selectedNode !== null) {
// have to flip the source coordinates since we did this for the existing connectors on the original tree
data = [{
source: {
x: selectedNode.y0,
y: selectedNode.x0
},
target: {
x: draggingNode.y0,
y: draggingNode.x0
}
}];
}
var link = svgGroup.selectAll(".templink").data(data);
link.enter().append("path")
.attr("class", "templink")
.attr("d", d3.svg.diagonal())
.attr('pointer-events', 'none');
link.attr("d", d3.svg.diagonal());
link.exit().remove();
};
// Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children.
function centerNode(source) {
var scale = (depthwidth === 10) ? zoomListener.scale() : 0.4;
var x = -source.y0;
var y = -source.x0;
x = x * scale + 150;
y = y * scale + viewerHeight / 2;
d3.select('g').transition()
.duration(duration)
.attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
zoomListener.scale(scale);
zoomListener.translate([x, y]);
}
// Toggle children function
function toggleChildren(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else if (d._children) {
d.children = d._children;
d._children = null;
}
return d;
}
// Toggle children on click.
function click(d) {
if (d3.event.defaultPrevented) return; // click suppressed
d = toggleChildren(d);
update(d);
//centerNode(d);
}
//arrow
baseSvg.append("svg:defs")
.append("svg:marker")
.attr("id", "arrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", 22)
.attr("refY", 5)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 6)
.attr("markerHeight", 9)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M 0 0 L 10 5 L 0 10 z");
//marker for input type graph
baseSvg.append("svg:defs")
.append("svg:marker")
.attr("id", "input-arrow")
.attr("viewBox", "0 0 10 10")
.attr("refX", -15)
.attr("refY", 5)
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 6)
.attr("markerHeight", 9)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M -2 5 L 8 0 L 8 10 z");
function update(source) {
// Compute the new height, function counts total children of root node and sets tree height accordingly.
// This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed
// This makes the layout more consistent.
var levelWidth = [1];
var childCount = function(level, n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level + 1] += n.children.length;
n.children.forEach(function(d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
tree = tree.nodeSize([50, 100]);
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Set widths between levels based on maxLabelLength.
nodes.forEach(function(d) {
if(levelWidth.length > 1 && depthwidth === 10){
for(var o=0; o < levelWidth.length; o++){
if(levelWidth[o] > 4 ) { depthwidth = 70; break;}
}
}
var maxLebal = maxLabelLength;
if(depthwidth === 10) { maxLebal = 20;}
d.y = (d.depth * (maxLebal * depthwidth));
});
// Update the nodes…
node = svgGroup.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.call(dragListener)
.attr("class", "node")
.attr("transform", function() {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on('click', click);
nodeEnter.append("image")
.attr("class","nodeImage")
.attr("xlink:href", function(d) {
return d.type === 'Table' ? '../img/tableicon.png' : '../img/process.png';
})
.on('mouseover', function(d) {
if (d.type === 'LoadProcess' || 'Table') {
tooltip.show(d);
}
})
.on('mouseout', function(d) {
if (d.type === 'LoadProcess' || 'Table') {
tooltip.hide(d);
}
})
.attr("x", "-18px")
.attr("y", "-18px")
.attr("width", "34px")
.attr("height", "34px");
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -10 : 10;
})
.attr("dx", function (d) { return d.children ? 50 : -50; })
.attr("dy", -24)
.attr('class', 'place-label')
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
var nameDis = (d.name.length > 15) ? d.name.substring(0,15) + "..." : d.name;
$(this).attr('title', d.name);
return nameDis;
})
.style("fill-opacity", 0);
// Update the text to reflect whether node has children or not.
node.select('text')
.attr("x", function(d) {
return d.children || d._children ? -10 : 10;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
var nameDis = (d.name.length > 15) ? d.name.substring(0,15) + "..." : d.name;
$(this).attr('title', d.name);
return nameDis;
});
// Change the circle fill depending on whether it has children and is collapsed
// Change the circle fill depending on whether it has children and is collapsed
node.select("image.nodeImage")
.attr("r", 4.5)
.attr("xlink:href", function(d) {
if(d._children){
return d.type === 'Table' ? '../img/tableicon1.png' : '../img/process1.png';
}
return d.type === 'Table' ? '../img/tableicon.png' : '../img/process.png';
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
// Fade the text in
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function() {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 0);
nodeExit.select("text")
.style("fill-opacity", 0);
// Update the links…
var link = svgGroup.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.style('stroke', 'green')
.attr("d", function() {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function() {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
if ($scope.type === 'inputs') {
link.attr("marker-start", "url(#input-arrow)"); //if input
} else {
link.attr("marker-end", "url(#arrow)"); //if input
}
}
// Append a group which holds all nodes and which the zoom Listener can act upon.
var svgGroup = baseSvg.append("g")
.attr("transform", "translate(120 ," + heightg/2 + ")");
// Define the root
root = data;
root.x0 = viewerHeight / 2;
root.y0 = 0;
// Layout the tree initially and center on the root node.
update(root);
centerNode(root);
var couplingParent1 = tree.nodes(root).filter(function(d) {
return d.name === 'cluster';
})[0];
var couplingChild1 = tree.nodes(root).filter(function(d) {
return d.name === 'JSONConverter';
})[0];
multiParents = [{
parent: couplingParent1,
child: couplingChild1
}];
multiParents.forEach(function() {
svgGroup.append("path", "g");
});
}
}
]);
......@@ -131,6 +131,8 @@ angular.module('dgc.lineage').controller('Lineage_ioController', ['$element', '$
}
$scope.onReset = function() {
$scope.height = $scope.initialHeight;
angular.element('.lineage-viz').height($scope.height);
renderGraph($scope.lineageData, {
eleObj: $element,
element: $element[0],
......@@ -237,9 +239,8 @@ angular.module('dgc.lineage').controller('Lineage_ioController', ['$element', '$
function renderGraph(data, container) {
// ************** Generate the tree diagram *****************
var element = d3.select(container.element),
widthg = Math.max(container.width, 1100),
heightg = Math.max((window.innerHeight - 400), 500),
widthg = container.width || 1100,
heightg = container.height || Math.max((window.innerHeight - 400), 300),
totalNodes = 0,
maxLabelLength = 0,
selectedNode = null,
......@@ -258,7 +259,8 @@ angular.module('dgc.lineage').controller('Lineage_ioController', ['$element', '$
var viewerWidth = widthg - 15,
viewerHeight = heightg;
viewerHeight = heightg,
center = [viewerWidth / 2, viewerHeight / 2];
var tree = d3.layout.tree().size([viewerHeight, viewerWidth]);
/*.size([viewerHeight, viewerWidth]); nodeSize([100, 200]);*/
......@@ -308,9 +310,9 @@ angular.module('dgc.lineage').controller('Lineage_ioController', ['$element', '$
// Sort the tree initially incase the JSON isn't in a sorted order.
sortTree();
// Define the zoom function for the zoomable tree
// Define the zoom function for the zoomable tree
function zoom() {
svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
d3.select('g').attr("transform", "translate(" + zoomListener.translate() + ")scale(" + zoomListener.scale() + ")");
}
// define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents
......@@ -334,6 +336,7 @@ angular.module('dgc.lineage').controller('Lineage_ioController', ['$element', '$
.attr("height", viewerHeight)
.attr("class", "overlay")
.call(zoomListener)
.call(zoomListener.event)
.on("dblclick.zoom", function() {
return null;
})
......@@ -708,6 +711,45 @@ angular.module('dgc.lineage').controller('Lineage_ioController', ['$element', '$
var svgGroup = baseSvg.append("g")
.attr("transform", "translate(0,0)");
// Simplest possible buttons
var intervalID;
d3.selectAll('.zoom-buttons').on('mousedown', function(){
d3.event.preventDefault();
$scope.factor = (this.id === 'zoom_in') ? 1.1 : 1/1.1;
intervalID = setInterval(zoom_by, 40, $scope.factor);
}).on('mouseup', function(){
d3.event.preventDefault();
clearInterval(intervalID);
intervalID = undefined;
});
function zoom_by(factor){
var scale = zoomListener.scale(),
extent = zoomListener.scaleExtent(),
translate = zoomListener.translate(),
x = translate[0], y = translate[1],
target_scale = scale * factor;
// If we're already at an extent, done
if (target_scale === extent[0] || target_scale === extent[1]) { return false; }
// If the factor is too much, scale it down to reach the extent exactly
var clamped_target_scale = Math.max(extent[0], Math.min(extent[1], target_scale));
if (clamped_target_scale !== target_scale){
target_scale = clamped_target_scale;
factor = target_scale / scale;
}
// Center each vector, stretch, then put back
x = (x - center[0]) * factor + center[0];
y = (y - center[1]) * factor + center[1];
// Enact the zoom immediately
zoomListener.scale(target_scale)
.translate([x,y]);
zoom();
}
// Define the root
root = data;
root.x0 = viewerWidth / 2;
......@@ -716,6 +758,11 @@ angular.module('dgc.lineage').controller('Lineage_ioController', ['$element', '$
// Layout the tree initially and center on the root node.
update(root);
centerNode(root);
if(!$scope.initialHeight){
$scope.initialHeight = angular.element('.lineage-viz').height();
}
angular.element('.lineage-viz').resizable({minWidth:1150, maxWidth:1150, maxHeight: angular.element('.lineage-viz').height(), minHeight:50
});
$scope.requested = false;
var couplingParent1 = tree.nodes(root).filter(function(d) {
return d.name === 'cluster';
......
......@@ -20,6 +20,8 @@
<button type="button" class="btn btn-primary pull-right" ng-click="onReset()">
Reset
</button>
<button type="button" class="btn btn-primary zoom-buttons pull-right" id="zoom_out"><i class="fa fa-minus"></i></button>
<button type="button" class="btn btn-primary zoom-buttons pull-right" id="zoom_in"><i class="fa fa-plus"></i> </button>
<div class="graph">
<h4 data-ng-if="!requested && !lineageData" class="alignLineage">No lineage data found</h4>
<i data-ng-if="requested" class="fa fa-spinner fa-spin fa-5x"></i>
......
......@@ -6,6 +6,7 @@ INCOMPATIBLE CHANGES:
ATLAS-379 Create sqoop and falcon metadata addons (venkatnrangan,bvellanki,sowmyaramesh via shwethags)
ALL CHANGES:
ATLAS-406 Resizing lineage window – should be an anchor on a corner – like ppt for graphic (sanjayp via shwethags)
ATLAS-432 QuickStart lineage is broken (yhemanth via shwethags)
ATLAS-421 typo in Architecture.twiki (dbist13 via shwethags)
ATLAS-387 Running quick_start without a valid atlas endpoint in configuration or argument prints a spurious success message (yhemanth via shwethags)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment