blob: 16b32a226126ea4268d45e53eca1ab45ad71506c [file] [log] [blame]
/*
* 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.
*/
/* global define, module, require, exports */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery',
'd3',
'nf.Common',
'nf.ng.Bridge',
'nf.Label',
'nf.Funnel',
'nf.Port',
'nf.RemoteProcessGroup',
'nf.ProcessGroup',
'nf.Processor',
'nf.Connection',
'nf.ConnectionConfiguration',
'nf.CanvasUtils',
'nf.Connectable',
'nf.Draggable',
'nf.Selectable',
'nf.ContextMenu',
'nf.QuickSelect'],
function ($, d3, nfCommon, nfNgBridge, nfLabel, nfFunnel, nfPort, nfRemoteProcessGroup, nfProcessGroup, nfProcessor, nfConnection, nfConnectionConfiguration, nfCanvasUtils, nfConnectable, nfDraggable, nfSelectable, nfContextMenu, nfQuickSelect) {
return (nf.Graph = factory($, d3, nfCommon, nfNgBridge, nfLabel, nfFunnel, nfPort, nfRemoteProcessGroup, nfProcessGroup, nfProcessor, nfConnection, nfConnectionConfiguration, nfCanvasUtils, nfConnectable, nfDraggable, nfSelectable, nfContextMenu, nfQuickSelect));
});
} else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = (nf.Graph =
factory(require('jquery'),
require('d3'),
require('nf.Common'),
require('nf.ng.Bridge'),
require('nf.Label'),
require('nf.Funnel'),
require('nf.Port'),
require('nf.RemoteProcessGroup'),
require('nf.ProcessGroup'),
require('nf.Processor'),
require('nf.Connection'),
require('nf.ConnectionConfiguration'),
require('nf.CanvasUtils'),
require('nf.Connectable'),
require('nf.Draggable'),
require('nf.Selectable'),
require('nf.ContextMenu'),
require('nf.QuickSelect')));
} else {
nf.Graph = factory(root.$,
root.d3,
root.nf.Common,
root.nf.ng.Bridge,
root.nf.Label,
root.nf.Funnel,
root.nf.Port,
root.nf.RemoteProcessGroup,
root.nf.ProcessGroup,
root.nf.Processor,
root.nf.Connection,
root.nf.ConnectionConfiguration,
root.nf.CanvasUtils,
root.nf.Connectable,
root.nf.Draggable,
root.nf.Selectable,
root.nf.ContextMenu,
root.nf.QuickSelect);
}
}(this, function ($, d3, nfCommon, nfNgBridge, nfLabel, nfFunnel, nfPort, nfRemoteProcessGroup, nfProcessGroup, nfProcessor, nfConnection, nfConnectionConfiguration, nfCanvasUtils, nfConnectable, nfDraggable, nfSelectable, nfContextMenu, nfQuickSelect) {
'use strict';
var combinePorts = function (contents) {
if (nfCommon.isDefinedAndNotNull(contents.inputPorts) && nfCommon.isDefinedAndNotNull(contents.outputPorts)) {
return contents.inputPorts.concat(contents.outputPorts);
} else if (nfCommon.isDefinedAndNotNull(contents.inputPorts)) {
return contents.inputPorts;
} else if (nfCommon.isDefinedAndNotNull(contents.outputPorts)) {
return contents.outputPorts;
} else {
return [];
}
};
var combinePortStatus = function (status) {
if (nfCommon.isDefinedAndNotNull(status.inputPortStatusSnapshots) && nfCommon.isDefinedAndNotNull(status.outputPortStatusSnapshots)) {
return status.inputPortStatusSnapshots.concat(status.outputPortStatusSnapshots);
} else if (nfCommon.isDefinedAndNotNull(status.inputPortStatusSnapshots)) {
return status.inputPortStatusSnapshots;
} else if (nfCommon.isDefinedAndNotNull(status.outputPortStatusSnapshots)) {
return status.outputPortStatusSnapshots;
} else {
return [];
}
};
/**
* Updates component visibility based on their proximity to the screen's viewport.
*/
var updateComponentVisibility = function () {
var canvasContainer = $('#canvas-container');
var translate = nfCanvasUtils.getCanvasTranslate();
var scale = nfCanvasUtils.getCanvasScale();
// scale the translation
translate = [translate[0] / scale, translate[1] / scale];
// get the normalized screen width and height
var screenWidth = canvasContainer.width() / scale;
var screenHeight = canvasContainer.height() / scale;
// calculate the screen bounds one screens worth in each direction
var screenLeft = -translate[0] - screenWidth;
var screenTop = -translate[1] - screenHeight;
var screenRight = screenLeft + (screenWidth * 3);
var screenBottom = screenTop + (screenHeight * 3);
// detects whether a component is visible and should be rendered
var isComponentVisible = function (d) {
if (!nfCanvasUtils.shouldRenderPerScale()) {
return false;
}
var left = d.position.x;
var top = d.position.y;
var right = left + d.dimensions.width;
var bottom = top + d.dimensions.height;
// determine if the component is now visible
return screenLeft < right && screenRight > left && screenTop < bottom && screenBottom > top;
};
// detects whether a connection is visible and should be rendered
var isConnectionVisible = function (d) {
if (!nfCanvasUtils.shouldRenderPerScale()) {
return false;
}
var x, y;
if (d.bends.length > 0) {
var i = Math.min(Math.max(0, d.labelIndex), d.bends.length - 1);
x = d.bends[i].x;
y = d.bends[i].y;
} else {
x = (d.start.x + d.end.x) / 2;
y = (d.start.y + d.end.y) / 2;
}
return screenLeft < x && screenRight > x && screenTop < y && screenBottom > y;
};
// marks the specific component as visible and determines if its entering or leaving visibility
var updateVisibility = function (d, isVisible) {
var selection = d3.select('#id-' + d.id);
var visible = isVisible(d);
var wasVisible = selection.classed('visible');
// mark the selection as appropriate
selection.classed('visible', visible)
.classed('entering', function () {
return visible && !wasVisible;
}).classed('leaving', function () {
return !visible && wasVisible;
});
};
// get the all components
var graph = nfGraph.get();
// update the visibility for each component
$.each(graph.processors, function (_, d) {
updateVisibility(d, isComponentVisible);
});
$.each(graph.ports, function (_, d) {
updateVisibility(d, isComponentVisible);
});
$.each(graph.processGroups, function (_, d) {
updateVisibility(d, isComponentVisible);
});
$.each(graph.remoteProcessGroups, function (_, d) {
updateVisibility(d, isComponentVisible);
});
$.each(graph.connections, function (_, d) {
updateVisibility(d, isConnectionVisible);
});
};
var nfGraph = {
init: function () {
// initialize the object responsible for each type of component
nfLabel.init(nfConnectable, nfDraggable, nfSelectable, nfContextMenu, nfQuickSelect);
nfFunnel.init(nfConnectable, nfDraggable, nfSelectable, nfContextMenu);
nfPort.init(nfConnectable, nfDraggable, nfSelectable, nfContextMenu, nfQuickSelect);
nfRemoteProcessGroup.init(nfConnectable, nfDraggable, nfSelectable, nfContextMenu, nfQuickSelect);
nfProcessGroup.init(nfConnectable, nfDraggable, nfSelectable, nfContextMenu);
nfProcessor.init(nfConnectable, nfDraggable, nfSelectable, nfContextMenu, nfQuickSelect);
nfConnection.init(nfSelectable, nfContextMenu, nfQuickSelect, nfConnectionConfiguration);
// display the deep link
return nfCanvasUtils.showDeepLink(true);
},
/**
* Populates the graph with the resources defined in the response.
*
* @argument {object} processGroupContents The contents of the process group
* @argument {boolean} selectAll Whether or not to select the new contents
*/
add: function (processGroupContents, options) {
var selectAll = false;
if (nfCommon.isDefinedAndNotNull(options)) {
selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll;
}
// if we are going to select the new components, deselect the previous selection
if (selectAll) {
nfCanvasUtils.getSelection().classed('selected', false);
}
// merge the ports together
var ports = combinePorts(processGroupContents);
// add the components to the responsible object
nfLabel.add(processGroupContents.labels, options);
nfFunnel.add(processGroupContents.funnels, options);
nfRemoteProcessGroup.add(processGroupContents.remoteProcessGroups, options);
nfPort.add(ports, options);
nfProcessGroup.add(processGroupContents.processGroups, options);
nfProcessor.add(processGroupContents.processors, options);
nfConnection.add(processGroupContents.connections, options);
// inform Angular app if the selection is changing
if (selectAll) {
nfNgBridge.digest();
}
},
/**
* Populates the graph with the resources defined in the response.
*
* @argument {object} processGroupContents The contents of the process group
* @argument {object} options Configuration options
*/
set: function (processGroupContents, options) {
var selectAll = false;
if (nfCommon.isDefinedAndNotNull(options)) {
selectAll = nfCommon.isDefinedAndNotNull(options.selectAll) ? options.selectAll : selectAll;
}
// if we are going to select the new components, deselect the previous selection
if (selectAll) {
nfCanvasUtils.getSelection().classed('selected', false);
}
// merge the ports together
var ports = combinePorts(processGroupContents);
// add the components to the responsible object
nfLabel.set(processGroupContents.labels, options);
nfFunnel.set(processGroupContents.funnels, options);
nfRemoteProcessGroup.set(processGroupContents.remoteProcessGroups, options);
nfPort.set(ports, options);
nfProcessGroup.set(processGroupContents.processGroups, options);
nfProcessor.set(processGroupContents.processors, options);
nfConnection.set(processGroupContents.connections, options);
// inform Angular app if the selection is changing
if (selectAll) {
nfNgBridge.digest();
}
},
/**
* Expires any caches prior to setting updated components via .set(...) above. This is necessary
* if an ajax request returns out of order. The caches will ensure that added/removed components
* will not be removed/added due to process group refreshes. Whether or not a component is present
* is ambiguous whether the request is from before the component was added/removed or if another
* client has legitimately removed/added it. Once a request is initiated after the component is
* added/removed we can remove the entry from the cache.
*
* @param timestamp expire caches before
*/
expireCaches: function (timestamp) {
nfLabel.expireCaches(timestamp);
nfFunnel.expireCaches(timestamp);
nfRemoteProcessGroup.expireCaches(timestamp);
nfPort.expireCaches(timestamp);
nfProcessGroup.expireCaches(timestamp);
nfProcessor.expireCaches(timestamp);
nfConnection.expireCaches(timestamp);
},
/**
* Gets the components currently on the canvas.
*/
get: function () {
return {
labels: nfLabel.get(),
funnels: nfFunnel.get(),
ports: nfPort.get(),
remoteProcessGroups: nfRemoteProcessGroup.get(),
processGroups: nfProcessGroup.get(),
processors: nfProcessor.get(),
connections: nfConnection.get()
};
},
/**
* Gets a graph component `type`.
*
* @param type The type of component.
*/
getComponentByType: function (type) {
switch (type)
{
case "Label":
return nfLabel;
break;
case "Funnel":
return nfFunnel;
break;
case "Port":
return nfPort;
break;
case "RemoteProcessGroup":
return nfRemoteProcessGroup;
break;
case "ProcessGroup":
return nfProcessGroup;
break;
case "Processor":
return nfProcessor;
break;
case "Connection":
return nfConnection;
break;
default:
throw new Error('Unknown component type.');
break;
}
},
/**
* Clears all the components currently on the canvas. This function does not automatically refresh.
*/
removeAll: function () {
// remove all the components
nfLabel.removeAll();
nfFunnel.removeAll();
nfPort.removeAll();
nfRemoteProcessGroup.removeAll();
nfProcessGroup.removeAll();
nfProcessor.removeAll();
nfConnection.removeAll();
},
/**
* Refreshes all components currently on the canvas.
*/
pan: function () {
// refresh the components
nfPort.pan();
nfRemoteProcessGroup.pan();
nfProcessGroup.pan();
nfProcessor.pan();
nfConnection.pan();
},
/**
* Updates component visibility based on the current translation/scale.
*/
updateVisibility: function () {
updateComponentVisibility();
nfGraph.pan();
// update URL deep linking params
nfCanvasUtils.setURLParameters();
},
/**
* Gets the currently selected components and connections.
*
* @returns {selection} The currently selected components and connections
*/
getSelection: function () {
return d3.selectAll('g.component.selected, g.connection.selected');
},
/**
* Reload the component on the canvas.
*
* @param component The component.
*/
reload: function (component) {
var componentData = component.datum();
if (componentData.permissions.canRead) {
if (nfCanvasUtils.isProcessor(component)) {
nfProcessor.reload(componentData.id);
} else if (nfCanvasUtils.isInputPort(component)) {
nfPort.reload(componentData.id);
} else if (nfCanvasUtils.isRemoteProcessGroup(component)) {
nfRemoteProcessGroup.reload(componentData.id);
}
}
}
};
return nfGraph;
}));