Arquivos
deepforge/src/visualizers/panels/PipelineEditor/PipelineEditorControl.js
T
Brian Broll e7d554578d Added eslint, jshint config. Fixes #99
WIP #99 Updated code style violations
2016-05-26 08:45:00 -05:00

428 linhas
16 KiB
JavaScript

/*globals define, WebGMEGlobal*/
/*jshint browser: true*/
/**
* Generated by VisualizerGenerator 1.7.0 from webgme on Thu May 19 2016 14:04:47 GMT-0500 (CDT).
*/
define([
'panels/EasyDAG/EasyDAGControl',
'common/core/coreQ',
'common/storage/constants',
'q',
'underscore'
], function (
EasyDAGControl,
Core,
STORAGE_CONSTANTS,
Q,
_
) {
'use strict';
var PipelineEditorControl;
PipelineEditorControl = function (options) {
EasyDAGControl.call(this, options);
this.addedIds = {};
};
_.extend(PipelineEditorControl.prototype, EasyDAGControl.prototype);
// TODO: Update the territory rules!
PipelineEditorControl.prototype.DEFAULT_DECORATOR = 'OperationDecorator';
PipelineEditorControl.prototype.TERRITORY_RULE = {children: 3};
PipelineEditorControl.prototype.selectedObjectChanged = function (nodeId) {
var desc = this._getObjectDescriptor(nodeId);
this._logger.debug('activeObject nodeId \'' + nodeId + '\'');
// Remove current territory patterns
if (this._currentNodeId) {
this._client.removeUI(this._territoryId);
}
this._currentNodeId = nodeId;
this._currentNodeParentId = undefined;
if (typeof this._currentNodeId === 'string') {
this._widget.setTitle(desc.name.toUpperCase());
if (typeof desc.parentId === 'string') {
this.$btnModelHierarchyUp.show();
} else {
this.$btnModelHierarchyUp.hide();
}
this._currentNodeParentId = desc.parentId;
// Put new node's info into territory rules
this.updateTerritory();
}
};
PipelineEditorControl.prototype.updateTerritory = function() {
var nodeId = this._currentNodeId;
// activeNode rules
this._territories = {};
this._territoryId = this._client.addUI(this, events => {
this._eventCallback(events);
});
this._territories[nodeId] = {children: 0}; // Territory "rule"
this._client.updateTerritory(this._territoryId, this._territories);
this._territories[nodeId] = this.TERRITORY_RULE;
// Add the operation definitions to the territory
var metanodes = this._client.getAllMetaNodes(),
operation = metanodes.find(n => n.getAttribute('name') === 'Operation');
// Get all the meta nodes that are instances of Operations
metanodes.map(n => n.getId())
.filter(nId => this._client.isTypeOf(nId, operation.getId()))
// Add a rule for them
.forEach(opId => this._territories[opId] = this.TERRITORY_RULE);
this._client.updateTerritory(this._territoryId, this._territories);
};
PipelineEditorControl.prototype.hasMetaName = function(id, name) {
var node = this._client.getNode(id),
bId = node.getBaseId(),
baseName;
while (bId) {
node = this._client.getNode(bId);
baseName = node.getAttribute('name');
if (baseName === name) {
return true;
}
bId = node.getBaseId();
}
return false;
};
PipelineEditorControl.prototype.formatIO = function(id) {
var node = this._client.getNode(id);
// This might not be necessary...
//return [
//node.getAttribute('name'),
//node.getBaseId()
//];
return node.getAttribute('name');
};
PipelineEditorControl.prototype.getOperationInputs = function(node) {
return this.getOperationData(node, 'Inputs');
};
PipelineEditorControl.prototype.getOperationOutputs = function(node) {
return this.getOperationData(node, 'Outputs');
};
PipelineEditorControl.prototype.getOperationData = function(node, type) {
var childrenIds = node.getChildrenIds(),
typeId = childrenIds.find(cId => this.hasMetaName(cId, type));
return typeId ? this._client.getNode(typeId).getChildrenIds() : [];
};
PipelineEditorControl.prototype._getObjectDescriptor = function(id) {
var desc = EasyDAGControl.prototype._getObjectDescriptor.call(this, id),
node = this._client.getNode(id);
if (this.hasMetaName(id, 'Operation')) {
// Only decorate operations in the currently active node
if (this._currentNodeId !== desc.parentId) {
return desc;
}
// Add inputs and outputs
var childrenIds = node.getChildrenIds(),
inputId = childrenIds.find(cId => this.hasMetaName(cId, 'Inputs')),
outputId = childrenIds.find(cId => this.hasMetaName(cId, 'Outputs')),
inputs,
outputs;
inputs = inputId ? this._client.getNode(inputId).getChildrenIds() : [];
outputs = outputId ? this._client.getNode(outputId).getChildrenIds() : [];
// Add the inputs, outputs in the form:
// [ name, baseId ]
desc.inputs = inputs.map(id => this.formatIO(id));
desc.outputs = outputs.map(id => this.formatIO(id));
// Remove the 'code' attribute
delete desc.attributes.code;
} else if (desc.isConnection) {
// Set src, dst to siblings and add srcPort, dstPort
desc.srcPort = desc.src;
desc.dstPort = desc.dst;
// Get the src/dst that are in the currentNode
desc.src = this.getSiblingContaining(desc.src);
desc.dst = this.getSiblingContaining(desc.dst);
if (desc.src === null || desc.dst === null) {
this._logger.warn(`Could not get src/dst for ${desc.id}`);
}
} else if (this.hasMetaName(desc.id, 'Data')) { // port
// Add nodeId for container
desc.nodeId = this.getSiblingContaining(desc.id);
// It is a data port if it has a parentId and the parent is either
// 'Inputs' or 'Outputs'
desc.isDataPort = desc.parentId &&
(this.hasMetaName(desc.parentId, 'Inputs') || this.hasMetaName(desc.parentId, 'Outputs'));
}
return desc;
};
PipelineEditorControl.prototype.getSiblingContaining = function(containedId) {
var n = this._client.getNode(containedId);
while (n && n.getParentId() !== this._currentNodeId) {
n = this._client.getNode(n.getParentId());
}
return n && n.getId();
};
PipelineEditorControl.prototype.isContainedInActive = function (gmeId) {
// Check if the given id is contained in the active node
return gmeId.indexOf(this._currentNodeId) === 0;
};
////////////////////// Node Load/Update/Unload Overrides //////////////////////
// Filter out the child nodes (bc of the larger territory)
PipelineEditorControl.prototype._onLoad = function (gmeId) {
var desc = this._getObjectDescriptor(gmeId);
if (desc.parentId === this._currentNodeId) {
this.addedIds[desc.id] = true;
return EasyDAGControl.prototype._onLoad.call(this, gmeId);
} else if (this.isContainedInActive(desc.parentId) && desc.isDataPort) {
// port added!
this.addedIds[desc.id] = true;
this._widget.addPort(desc);
}
};
PipelineEditorControl.prototype._onUnload = function (gmeId) {
// Check if it has been added
if(this.addedIds[gmeId]) {
delete this.addedIds[gmeId];
return EasyDAGControl.prototype._onUnload.call(this, gmeId);
}
};
PipelineEditorControl.prototype._onUpdate = function (gmeId) {
var desc = this._getObjectDescriptor(gmeId);
if (desc.isDataPort && this.isContainedInActive(desc.parentId)) { // port added!
this._widget.updatePort(desc);
} else if (desc.isConnection) {
this._widget.updateConnection(desc);
} else if (desc.parentId === this._currentNodeId) {
this._widget.updateNode(desc);
} // Ignore any other updates - ie, Inputs/Outputs containers
};
// Override the getSuccessors method to look up successors by operations
// with input nodes of the selected node's output type (prioritize the
// valid nodes that are using an unused output type, if one exists, ow
// prioritize based on current outgoing connections count).
// TODO
PipelineEditorControl.prototype.hasValidOutputs = function (inputId, outputs) {
return this.getValidOutputs(inputId, outputs);
};
PipelineEditorControl.prototype.getValidOutputs = function (inputId, outputs) {
// Valid input if one of the isTypeOf(<output>, inputId)
// for at least one output
var inputType = this._client.getNode(inputId).getMetaTypeId();
return outputs.filter(type => this._client.isTypeOf(type, inputType)).length;
};
PipelineEditorControl.prototype._getValidSuccessorNodes = function (nodeId) {
// Get all valid children
var node = this._client.getNode(nodeId),
children,
outputs;
children = this._getAllValidChildren(node.getParentId())
.map(id => this._client.getNode(id));
// Get all valid data output types of 'nodeId'
outputs = this.getOperationOutputs(node)
.map(id => this._client.getNode(id).getMetaTypeId());
// For all valid children, return all that have at least one
// (unoccupied) input that is a superclass (or same class) as
// one of the outputs
return children
.filter(node => this.getOperationInputs(node)
.filter(id => this.hasValidOutputs(id, outputs)).length)
.map(node => {
return {
node: this._getObjectDescriptor(node.getId())
};
});
};
PipelineEditorControl.prototype._getValidInitialNodes = function () {
// Get all nodes that have no inputs
return this._getAllValidChildren(this._currentNodeId)
.map(id => this._client.getNode(id))
.filter(node => !node.isAbstract() && !node.isConnection())
// Checking the name (below) is simply convenience so we can
// still create operation prototypes from Operation (which we
// wouldn't be able to do if it was abstract - which it probably
// should be)
.filter(node => node.getAttribute('name') !== 'Operation' &&
this.getOperationInputs(node).length === 0)
.map(node => this._getObjectDescriptor(node.getId()));
};
PipelineEditorControl.prototype._getPortPairs = function (outputs, inputs) {
// Given a set of outputs and (potential) inputs, return valid pairs
// <outputId, inputId> where `outputId` is the id of an outgoing port
// in the src operation and `inputId` is the id of an incoming port in
// the dst operation
var result = [],
ipairs = inputs.map(id => [id, this._client.getNode(id).getMetaTypeId()]),
oType;
// For each output, get all possible (valid) input destinations
outputs.forEach(outputId => {
oType = this._client.getNode(outputId).getMetaTypeId();
result = result.concat(ipairs.filter(pair =>
// output type should be valid input type
this._client.isTypeOf(oType, pair[1])
)
.map(pair => [outputId, pair[0]]) // Get the input data id
);
});
return result;
};
PipelineEditorControl.prototype.getConnectionId = function () {
return this._client.getAllMetaNodes()
.find(node => node.isConnection()).getId();
};
PipelineEditorControl.prototype._createConnectedNode = function (nodeId, typeId) {
// Create a node of type "typeId" after "nodeId"
// Figure out which ports need to be connected
var parentId = this._currentNodeId,
outputs = this.getOperationOutputs(this._client.getNode(nodeId)),
inputs = this.getOperationInputs(this._client.getNode(typeId)),
pairs = this._getPortPairs(outputs, inputs),
srcOpName = this._client.getNode(nodeId).getAttribute('name');
this._logger.info(`Valid ports for ${nodeId} -> ${typeId} are ${pairs}`);
// If none, => error!
// For now, I am assuming that they used '_getValidSuccessorNodes' to
// get the pairs. ie, it is valid.
// TODO
if (pairs.length === 1) { // If one, continue
var pair = pairs[0],
srcPortId = pair[0],
srcPort,
dstPortBaseId = pair[1],
dstPortBase,
rootGuid = this._client.getActiveRootHash(),
branchName = this._client.getActiveBranchName(),
startCommit = this._client.getActiveCommitHash(),
connTypeId = this.getConnectionId(),
project = this._client.getProjectObject(),
conn,
connBase,
parentNode,
commitMsg,
root;
// FIXME: This should use the core...
// For now, I am going to try to load the core and use it here...
var core = new Core(project, {
globConf: WebGMEGlobal.gmeConfig,
logger: this._logger.fork('core')
});
//this._client.startTransaction();
// Load the first node/commit...
core.loadRoot(rootGuid)
.then(_root => {
root = _root;
return Q.all(
[parentId, typeId, connTypeId, dstPortBaseId, srcPortId].map(id => core.loadByPath(root, id))
);
})
.then(nodes => {
// Create the given dst operation
var opBase = nodes[1],
dstOp;
parentNode = nodes[0];
connBase = nodes[2];
dstPortBase = nodes[3];
srcPort = nodes[4];
// Create the given dst operation
dstOp = core.createNode({
parent: parentNode,
base: opBase
});
commitMsg = `Adding ${core.getAttribute(dstOp, 'name')} after ${srcOpName}`;
return core.loadChildren(dstOp);
})
.then(containers => {
var inputContainer;
// Get the operation inputs (can't use the earlier fn - different node types)
inputContainer = containers
.find(cntr => core.isInstanceOf(cntr, 'Inputs'));
return core.loadChildren(inputContainer);
})
.then(inputDataPorts => {
// Get the matching input node
var dstPort = inputDataPorts.find(port => core.isTypeOf(port, dstPortBase));
// Create the connection
conn = core.createNode({
parent: parentNode,
base: connBase
});
// Connect srcPortId and the node from above
core.setPointer(conn, 'src', srcPort);
core.setPointer(conn, 'dst', dstPort);
var persisted = core.persist(root);
return project.makeCommit(
branchName,
[ startCommit ],
persisted.rootHash,
persisted.objects,
commitMsg
);
})
.then(result => {
if (result.status === STORAGE_CONSTANTS.SYNCED) {
// Throw out the changes... warn the user?
this._logger.info('SYNCED!');
} else {
// Throw out the changes... warn the user?
this._logger.warn(`Could not create operation after ${srcOpName}`);
}
})
.fail(err => this._logger.error(`Could not create operation after ${srcOpName}: ${err}`));
} else if (pairs.length > 1) {
// Else, prompt!
// TODO
}
};
return PipelineEditorControl;
});