| import {Component, ViewEncapsulation, AfterViewInit} from "@angular/core"; |
| import {TaskService} from "../../../services/task.service"; |
| import {TaskType} from "../../../models/task/type/task.type.model"; |
| import {OperationType} from "../../../models/task/type/operation/operation.type.model"; |
| import {OperationInPort} from "../../../models/task/type/operation/operation.inport.type.model"; |
| import {OperationOutPort} from "../../../models/task/type/operation/operation.outport.type.model"; |
| import {WorkflowService} from "../../../services/workflow.service"; |
| import {Router} from "@angular/router"; |
| |
| declare let mxClient: any; |
| declare let mxUtils: any; |
| declare let mxToolbar: any; |
| declare let mxDivResizer: any; |
| declare let mxGraph: any; |
| declare let mxDragSource: any; |
| declare let mxRubberband: any; |
| declare let mxKeyHandler: any; |
| declare let mxCodec: any; |
| declare let mxConstants: any; |
| declare let mxEdgeStyle: any; |
| declare let mxEvent: any; |
| declare let mxForm: any; |
| declare let mxCellAttributeChange: any; |
| declare let mxRectangle: any; |
| declare let mxPoint: any; |
| |
| @Component({ |
| templateUrl: './create.html', |
| encapsulation: ViewEncapsulation.None, |
| providers: [TaskService, WorkflowService] |
| }) |
| export class WorkflowCreateComponent implements AfterViewInit { |
| |
| taskTypes: Array<TaskType> = []; |
| operationTypes: Array<OperationType> = []; |
| graphInstance: any; |
| workFlowName: string = "Sample Workflow"; |
| |
| constructor(private taskService: TaskService, private workflowService: WorkflowService, private router: Router) { |
| |
| let startType: OperationType = new OperationType(); |
| startType.id = 1; |
| startType.name = "Start"; |
| startType.icon = "assets/icons/start.png"; |
| startType.outPorts.push(new OperationOutPort(1, "Output")); |
| |
| let endType: OperationType = new OperationType(); |
| endType.id = 1; |
| endType.name = "Stop"; |
| endType.icon = "assets/icons/stop.png"; |
| endType.inPorts.push(new OperationInPort(1, "Input")); |
| |
| this.operationTypes.push(startType, endType); |
| } |
| |
| loadGraphData() { |
| this.taskService.getAllTaskTypes().subscribe(data => { |
| this.taskTypes = data; |
| this.loadGraph(document.getElementById('graphContainer'), document.getElementById('toolContainer')); |
| }, err => { |
| |
| }) |
| } |
| |
| onShowWorkflowXMLClicked() { |
| let encoder = new mxCodec(); |
| let node = encoder.encode(this.graphInstance.getModel()); |
| mxUtils.popup(mxUtils.getPrettyXml(node), true); |
| } |
| |
| onCreateWorkflowClicked() { |
| let encoder = new mxCodec(); |
| let node = encoder.encode(this.graphInstance.getModel()); |
| this.workflowService.createWorkflow(this.workFlowName, mxUtils.getPrettyXml(node)).subscribe(data => { |
| alert("Workflow created"); |
| this.routeToListPage(); |
| }, err => { |
| alert("An error occurred"); |
| }); |
| } |
| |
| routeToListPage() { |
| this.router.navigateByUrl("/workflow"); |
| } |
| |
| ngAfterViewInit(): void { |
| this.loadGraphData(); |
| } |
| |
| loadGraph(container, tbContainer) { |
| |
| if (!mxClient.isBrowserSupported()) { |
| // Displays an error message if the browser is not supported. |
| mxUtils.error('Browser is not supported!', 200, false); |
| } else { |
| let toolbar = new mxToolbar(tbContainer); |
| toolbar.enabled = false; |
| |
| // Workaround for Internet Explorer ignoring certain styles |
| if (mxClient.IS_QUIRKS) { |
| document.body.style.overflow = 'hidden'; |
| new mxDivResizer(tbContainer); |
| new mxDivResizer(container); |
| } |
| |
| let doc = mxUtils.createXmlDocument(); |
| |
| let relation = doc.createElement('Edge'); |
| |
| let graph = new mxGraph(container); |
| this.graphInstance = graph; |
| |
| graph.dropEnabled = true; |
| |
| mxDragSource.prototype.getDropTarget = function (graph, x, y) { |
| let cell = graph.getCellAt(x, y); |
| |
| if (!graph.isValidDropTarget(cell)) { |
| cell = null; |
| } |
| |
| return cell; |
| }; |
| |
| // Enables new connections in the graph |
| graph.setConnectable(true); |
| graph.setMultigraph(false); |
| |
| let addTaskType = function (icon, w, h, taskType: TaskType) { |
| addToolbarItem(graph, toolbar, createTaskItem(doc, taskType), icon, doc); |
| }; |
| |
| let addOperation = function (icon, w, h, operationType: OperationType) { |
| addToolbarItem(graph, toolbar, createOperation(doc, operationType), icon, doc); |
| }; |
| |
| this.taskTypes.forEach((taskType) => { |
| addTaskType(taskType.icon, 40, 40, taskType); |
| }); |
| |
| this.operationTypes.forEach((operationType) => { |
| addOperation(operationType.icon, 40, 40, operationType); |
| }); |
| |
| toolbar.addLine(); |
| |
| graph.setCellsResizable(false); |
| graph.setResizeContainer(true); |
| graph.minimumContainerSize = new mxRectangle(0, 0, 500, 380); |
| graph.setBorder(60); |
| |
| new mxKeyHandler(graph); |
| |
| // Overrides method to disallow edge label editing |
| graph.isCellEditable = function (cell) { |
| return !this.getModel().isEdge(cell); |
| }; |
| |
| // Overrides method to provide a cell label in the display |
| graph.convertValueToString = function (cell) { |
| if (mxUtils.isNode(cell.value)) { |
| if (cell.value.nodeName.toLowerCase() == 'processingelement') { |
| return cell.getAttribute('name', ''); |
| } |
| if (cell.value.nodeName.toLowerCase() == 'operation') { |
| return cell.getAttribute('name', ''); |
| } |
| else if (cell.value.nodeName.toLowerCase() == 'edge') { |
| return ''; |
| } |
| |
| } |
| return ''; |
| }; |
| |
| // Overrides method to store a cell label in the model |
| let cellLabelChanged = graph.cellLabelChanged; |
| graph.cellLabelChanged = function (cell, newValue, autoSize) { |
| if (mxUtils.isNode(cell.value) && |
| cell.value.nodeName.toLowerCase() == 'processingelement') { |
| // Clones the value for correct undo/redo |
| let elt = cell.value.cloneNode(true); |
| |
| elt.setAttribute('name', newValue); |
| newValue = elt; |
| autoSize = true; |
| } |
| |
| cellLabelChanged.apply(this, arguments); |
| }; |
| |
| // Overrides method to create the editing value |
| let getEditingValue = graph.getEditingValue; |
| graph.getEditingValue = function (cell) { |
| if (mxUtils.isNode(cell.value) && |
| cell.value.nodeName.toLowerCase() == 'processingelement') { |
| return cell.getAttribute('name', ''); |
| } |
| }; |
| |
| new mxRubberband(graph); |
| |
| // Changes the style for match the markup |
| // Creates the default style for vertices |
| let style = graph.getStylesheet().getDefaultVertexStyle(); |
| style[mxConstants.STYLE_STROKECOLOR] = 'gray'; |
| style[mxConstants.STYLE_ROUNDED] = true; |
| style[mxConstants.STYLE_SHADOW] = true; |
| style[mxConstants.STYLE_FILLCOLOR] = '#DFDFDF'; |
| style[mxConstants.STYLE_GRADIENTCOLOR] = 'white'; |
| style[mxConstants.STYLE_FONTCOLOR] = 'black'; |
| style[mxConstants.STYLE_FONTSIZE] = '12'; |
| style[mxConstants.STYLE_SPACING] = 4; |
| |
| // Creates the default style for edges |
| style = graph.getStylesheet().getDefaultEdgeStyle(); |
| style[mxConstants.STYLE_STROKECOLOR] = '#0C0C0C'; |
| style[mxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = 'white'; |
| style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector; |
| style[mxConstants.STYLE_ROUNDED] = true; |
| style[mxConstants.STYLE_FONTCOLOR] = 'black'; |
| style[mxConstants.STYLE_FONTSIZE] = '10'; |
| |
| let parent = graph.getDefaultParent(); |
| graph.getModel().beginUpdate(); |
| try { |
| //var v1 = graph.insertVertex(parent, null, pe1, 40, 40, 80, 30); |
| //var v2 = graph.insertVertex(parent, null, pe2, 200, 150, 80, 30); |
| //var e1 = graph.insertEdge(parent, null, relation, v1, v2); |
| } finally { |
| // Updates the display |
| graph.getModel().endUpdate(); |
| } |
| |
| // Implements a properties panel that uses |
| // mxCellAttributeChange to change properties |
| graph.getSelectionModel().addListener(mxEvent.CHANGE, function (sender, evt) { |
| selectionChanged(graph); |
| }); |
| |
| selectionChanged(graph); |
| |
| } |
| |
| /** |
| * Updates the properties panel |
| */ |
| function selectionChanged(graph) { |
| let div = document.getElementById('properties'); |
| |
| // Forces focusout in IE |
| graph.container.focus(); |
| |
| // Clears the DIV the non-DOM way |
| div.innerHTML = ''; |
| |
| // Gets the selection cell |
| let cell = graph.getSelectionCell(); |
| |
| if (cell == null) { |
| mxUtils.writeln(div, 'Nothing selected.'); |
| } |
| else if (cell.value) { |
| // Writes the title |
| let center = document.createElement('center'); |
| mxUtils.writeln(center, cell.value.nodeName + ' (' + cell.id + ')'); |
| div.appendChild(center); |
| mxUtils.br(div); |
| |
| // Creates the form from the attributes of the user object |
| let form = new mxForm(); |
| |
| let attrs = cell.value.attributes; |
| |
| for (let i = 0; i < attrs.length; i++) { |
| if (!attrs[i].nodeName.startsWith("in-") && !attrs[i].nodeName.startsWith("out-")) { |
| createTextField(graph, form, cell, attrs[i]); |
| } |
| } |
| |
| div.appendChild(form.getTable()); |
| mxUtils.br(div); |
| } |
| } |
| |
| function createTaskItem(doc, taskType: TaskType) { |
| |
| var pe = doc.createElement('ProcessingElement'); |
| pe.setAttribute('name', taskType.name); |
| pe.setAttribute('Type', taskType.id); |
| |
| taskType.inputTypes.forEach((input, index) => { |
| pe.setAttribute(input.name, input.defaultValue); |
| }); |
| |
| taskType.outputTypes.forEach((output, index) => { |
| pe.setAttribute('output-' + output.name, ''); |
| }); |
| |
| taskType.outPorts.forEach((outPort, index) => { |
| pe.setAttribute("out-" + outPort.id, outPort.name); |
| }); |
| |
| pe.setAttribute("in-1" , "Input"); |
| |
| return pe; |
| } |
| |
| function createOperation(doc, operation: OperationType) { |
| var op = doc.createElement('Operation'); |
| op.setAttribute("name", operation.name); |
| operation.outPorts.forEach((outPort, index) => { |
| op.setAttribute("out-" + outPort.id, outPort.name); |
| }); |
| operation.inPorts.forEach((inPort, index) => { |
| op.setAttribute("in-" + inPort.id, inPort.name); |
| }); |
| return op; |
| } |
| |
| function createTextField(graph, form, cell, attribute) { |
| let input = form.addText(attribute.nodeName + ':', attribute.nodeValue); |
| |
| let applyHandler = function () { |
| let newValue = input.value || ''; |
| let oldValue = cell.getAttribute(attribute.nodeName, ''); |
| |
| if (newValue != oldValue) { |
| graph.getModel().beginUpdate(); |
| |
| try { |
| let edit = new mxCellAttributeChange( |
| cell, attribute.nodeName, |
| newValue); |
| graph.getModel().execute(edit); |
| graph.updateCellSize(cell); |
| } |
| finally { |
| graph.getModel().endUpdate(); |
| } |
| } |
| }; |
| |
| mxEvent.addListener(input, 'keypress', function (evt) { |
| // Needs to take shift into account for textareas |
| if (evt.keyCode == /*enter*/13 && !mxEvent.isShiftDown(evt)) { |
| input.blur(); |
| } |
| }); |
| |
| if (mxClient.IS_IE) { |
| mxEvent.addListener(input, 'focusout', applyHandler); |
| } |
| else { |
| // Note: Known problem is the blurring of fields in |
| // Firefox by changing the selection, in which case |
| // no event is fired in FF and the change is lost. |
| // As a workaround you should use a local variable |
| // that stores the focused field and invoke blur |
| // explicitely where we do the graph.focus above. |
| mxEvent.addListener(input, 'blur', applyHandler); |
| } |
| } |
| |
| function addToolbarItem(graph, toolbar, prototype, image, doc) { |
| // Function that is executed when the image is dropped on |
| // the graph. The cell argument points to the cell under |
| // the mousepointer if there is one. |
| let funct = function (graph, evt, cell, x, y) { |
| |
| let parent = graph.getDefaultParent(); |
| let model = graph.getModel(); |
| |
| let v1 = null; |
| |
| model.beginUpdate(); |
| |
| try { |
| v1 = graph.insertVertex(parent, null, prototype.cloneNode(true), x, y, 80, 60); |
| v1.setConnectable(false); |
| |
| let inputs = []; |
| let inputKeys = []; |
| let outputs = []; |
| let outputKeys = []; |
| for (let i = 0; i < prototype.attributes.length; i++) { |
| let attr = prototype.attributes[i]; |
| if (attr.nodeName.startsWith("in-")) { |
| inputs.push(attr.nodeValue); |
| inputKeys.push(attr.nodeName) |
| } |
| if (attr.nodeName.startsWith("out-")) { |
| outputs.push(attr.nodeValue); |
| outputKeys.push(attr.nodeName); |
| } |
| } |
| |
| let inputDivision = 1 / (inputs.length + 1); |
| let outputDivision = 1 / (outputs.length + 1); |
| |
| inputs.forEach(function (input, index) { |
| let port = doc.createElement('InPort'); |
| port.setAttribute('name', input); |
| port.setAttribute("port-id", inputKeys[index]); |
| |
| let v11 = graph.insertVertex(v1, null, port, 0, (index * inputDivision + inputDivision), 10, 10); |
| v11.geometry.offset = new mxPoint(-5, -5); |
| v11.geometry.relative = true; |
| }); |
| |
| outputs.forEach(function (output, index) { |
| let port = doc.createElement('OutPort'); |
| port.setAttribute('name', output); |
| port.setAttribute("port-id", outputKeys[index]); |
| |
| let v11 = graph.insertVertex(v1, null, port, 1, (index * outputDivision + outputDivision), 10, 10); |
| v11.geometry.offset = new mxPoint(-5, -5); |
| v11.geometry.relative = true; |
| }); |
| } |
| finally { |
| // Updates the display |
| graph.getModel().endUpdate(); |
| } |
| |
| graph.updateCellSize(v1); |
| graph.setSelectionCell(v1); |
| }; |
| |
| // Creates the image which is used as the drag icon (preview) |
| let img = toolbar.addMode(null, image, funct); |
| mxUtils.makeDraggable(img, graph, funct); |
| } |
| }; |
| } |