UNPKG

processmaker-builder

Version:

The gulp task runner for ProcessMaker building

1,438 lines (1,333 loc) 69.8 kB
var PMCanvas = function (options) { PMUI.draw.Canvas.call(this, options); this.project = null; this.items = null; /** * Minimum distance to "snap" to a guide * @type {number} */ this.MIN_DISTANCE = 4; /** * Array which contains a list of all coordinates to snap * @type {number} */ this.guides = []; this.attachedListeners = null; this.hasClickEvent = false; this.isDragging = false; this.isGridLine = true; this.dragConnectHandlers = new PMUI.util.ArrayList(); this.dropConnectHandlers = new PMUI.util.ArrayList(); this.isDraggingConnectHandler = false; this.businessObject = {}; this.isMouseOverHelper = false; this.canConnect = false; this.canCreateShape = false; this.canCreateShapeType = null; this.canCreateShapeClass = null; this.shapeHelper = null; this.coronaClick = false; this.connectStartShape = null; this.coronaShape = null; this.lassoEnabled = false; this.lassoLimits = null; PMCanvas.prototype.init.call(this, options); }; PMCanvas.prototype = new PMUI.draw.Canvas(); PMCanvas.prototype.type = "PMCanvas"; this.canvasContainerBehavior = null; PMCanvas.prototype.init = function (options) { var defaults = { project: null, snapToGuide: true, enabledMenu: false, hasClickEvent: false }; jQuery.extend(true, defaults, options); this.setProject(defaults.project) .setEnabledMenu(defaults.enabledMenu) .setHasClickEvent(defaults.hasClickEvent) .setSnapToGuide(defaults.snapToGuide); this.items = new PMUI.util.ArrayList(); this.attachedListeners = false; }; PMCanvas.prototype.setHasClickEvent = function (value) { this.hasClickEvent = value; return this; }; PMCanvas.prototype.setEnabledMenu = function (value) { this.enabledMenu = value; return this; }; PMCanvas.prototype.setParent = function (parent) { this.parent = parent; return this; }; PMCanvas.prototype.setProject = function (project) { if (project instanceof PMProject) { this.project = project; } return this; }; PMCanvas.prototype.onCreateElementHandler = function (element) { var id, label, menuElement, shapeElement; if (this.project) { this.project.addElement(element); if (!this.project.loadingProcess) { if (element.relatedObject && (element.relatedObject.type === 'PMPool' || element.relatedObject.type === 'PMActivity')) { element.relatedObject.canvas.emptyCurrentSelection(); element.relatedObject.canvas.addToSelection(element.relatedObject); } } if (element.type === "Connection") { return; } } }; /** * Factory of canvas behaviors. It uses lazy instantiation to create * instances of the different container behaviors * @param {String} type An string that specifies the container behavior we want * an instance to have, it can be regular or nocontainer * @return {ContainerBehavior} */ PMCanvas.prototype.containerBehaviorFactory = function (type) { if (type === 'pmcanvas') { if (!this.canvasContainerBehavior) { this.canvasContainerBehavior = new CanvasContainerBehavior(); } return this.canvasContainerBehavior; } else { return PMShape.prototype.containerBehaviorFactory.call(this, type); } }; PMCanvas.prototype.dropBehaviorFactory = function (type, selectors) { if (type === 'canvasdrop') { if (!this.pmConnectionDropBehavior) { this.pmConnectionDropBehavior = new PMContainerDropBehavior(selectors); } return this.pmConnectionDropBehavior; } else { return PMUI.draw.CustomShape.prototype.dropBehaviorFactory.call(this, type, selectors); } }; PMCanvas.prototype.triggerTextChangeEvent = function (element, oldText, newText) { var valid, reg, e, nText, mp, id; if (element.parent.getType() === 'PMActivity' && !this.validateName(element, newText, oldText)) { newText = oldText; } reg = /<[^\s]/g; nText = newText.trim(); e = reg.test(nText); if (e) { nText = nText.replace(/</g, '< '); } this.updatedElement = [{ id: element.parent.id, type: element.parent.type, parent: element.parent, fields: [{ field: "name", oldVal: oldText, newVal: nText }] }]; element.parent.setName(nText); element.updateDimension(); element.parent.setBPPMName(nText); if (element.parent.atachedDiagram) { id = PMDesigner.canvasList.getID(); $('#' + id + ' option[value=' + element.parent.atachedDiagram.getID() + ']') .text(nText); } jQuery(this.html).trigger("changeelement"); }; PMCanvas.prototype.triggerConnectionStateChangeEvent = function (connection) { var points = [], Point = PMUI.util.Point, point, i; for (i = 0; i < connection.points.length; i += 1) { point = connection.points[i]; points.push(new Point(point.x / this.zoomFactor, point.y / this.zoomFactor)); } this.updatedElement = [{ id: connection.getID(), type: connection.type, fields: [ { field: 'state', oldVal: connection.getOldPoints(), newVal: points } ], relatedObject: connection }]; connection.algorithm = 'user'; $(this.html).trigger('changeelement'); this.hideDragConnectHandlers(); return this; }; PMCanvas.prototype.triggerUserStateChangeEvent = function (connection) { var points = [], Point = PMUI.util.Point, point, i; for (i = 0; i < connection.points.length; i += 1) { point = connection.points[i]; points.push(new Point(point.x / this.zoomFactor, point.y / this.zoomFactor)); } this.updatedElement = [{ id: connection.getID(), type: connection.type, fields: [ { field: 'state', oldVal: connection.getOldPoints(), newVal: points } ], relatedObject: connection }]; connection.algorithm = 'user'; return this; }; PMCanvas.prototype.updateDimensionLabel = function (element) { var width, width = element.relatedObject.width; newWidth = Math.max(width, this.zoomWidth); element.relatedObject.label.setWidth(width); return this; }; PMCanvas.prototype.onChangeElementHandler = function (element) { var textNode, currentElement; if (this.project && element.length > 0) { try { this.project.updateElement(element); } catch (e) { throw new Error("Error, There are problems updating the element".translate(), e); } } }; PMCanvas.prototype.onRemoveElementHandler = function (elements) { var i, element, shapeElement; if (this.project) { this.project.removeElement(elements); try { for (i = 0; i < elements.length; i += 1) { if (elements[i].type === "Connection") { element = elements[i]; element.updateIncomingAndOutgoingConnections("remove"); shapeElement = element.destPort.getParent(); if (shapeElement instanceof PMGateway) { shapeElement.evaluateGatewayDirection(); } shapeElement = element.srcPort.getParent(); if (shapeElement instanceof PMGateway) { shapeElement.evaluateGatewayDirection(); } PMDesigner.project.updateElement([]); break; } } } catch (e) { throw new Error("Error, There are problems removing the element".translate(), e); } } }; PMCanvas.prototype.onSelectElementHandler = function (element) { PMUI.removeCurrentMenu(); if (element.length === 1) { switch (element[0].type) { case 'PMActivity': case 'PMEvent': case 'PMGateway': break; } } if (this.currentLabel != null) { this.hideAllFocusedLabels(); } this.isSelected = true; return this; }; PMCanvas.prototype.defineEvents = function () { return PMUI.draw.Canvas.prototype.defineEvents.call(this); }; PMCanvas.prototype.getContextMenu = function () { return {}; }; PMCanvas.prototype.onRightClick = function () { var that = this; return function (a, b, c) { }; }; /** * Set guide Lines to canvas and create vertican and horizontal snappers * @param {Boolean} snap new value to verify if canvas has enabled snappes * @chainable */ PMCanvas.prototype.setSnapToGuide = function (snap) { this.snapToGuide = snap; // create snappers this.horizontalSnapper = new PMSnapper({ orientation: 'horizontal', canvas: this, width: 4000, height: 1 }); this.verticalSnapper = new PMSnapper({ orientation: 'vertical', canvas: this, width: 1, height: 4000 }); return this; }; /** * Build the data of the snappers recreating the arrays, * this method is called from {@link RegularDragBehavior#onDragStart} (it might * be an overrided method `onDragStart` if the instance of {@link RegularDragBehavior} was changed). * @chainable */ PMCanvas.prototype.startSnappers = function (event) { var shape, i, parent; this.horizontalSnapper.getHTML(); this.verticalSnapper.getHTML(); this.guides = []; for (i = 0; i < this.customShapes.getSize(); i += 1) { shape = this.customShapes.get(i); if (!this.currentSelection.find('id', shape.getID())) { this.computeGuidesForElement(shape); } } return this; }; PMCanvas.prototype.computeGuidesForElement = function (shape) { var x = shape.getHTML().offsetLeft, y = shape.getHTML().offsetTop, w, h; w = shape.getZoomWidth() - 1; h = shape.getZoomHeight() - 1; this.guides.push( {type: "h", x: x, y: y}, {type: "h", x: x, y: y + h}, {type: "v", x: x, y: y}, {type: "v", x: x + w, y: y} ); return this; }; /** * Process the snappers according to this criteria and show and hide: * * - To show the vertical snapper * - `shape.absoluteX` must equal a value in the data of `this.verticalSnapper` * - `shape.absoluteX + shape.width` must equal a value in the data of `this.verticalSnapper` * * - To show the horizontal snapper * - `shape.absoluteY` must equal a value in the data of `this.horizontalSnapper` * - `shape.absoluteY + shape.height` must equal a value in the data of `this.horizontalSnapper` * * @param {Object} e * @parem {Object} ui * @param {Shape} customShape * @chainable */ PMCanvas.prototype.processGuides = function (e, ui, customShape) { // iterate all guides, remember the closest h and v guides var guideV, guideH, distV = this.MIN_DISTANCE + 1, distH = this.MIN_DISTANCE + 1, offsetV, offsetH, mouseRelX, mouseRelY, pos, w = customShape.getZoomWidth() - 1, h = customShape.getZoomHeight() - 1, d; mouseRelY = e.originalEvent.pageY - ui.offset.top; mouseRelX = e.originalEvent.pageX - ui.offset.left; pos = { top: e.originalEvent.pageY - customShape.canvas.getY() - mouseRelY + customShape.canvas.getTopScroll(), left: e.originalEvent.pageX - customShape.canvas.getX() - mouseRelX + customShape.canvas.getLeftScroll() }; $.each(this.guides, function (i, guide) { if (guide.type === "h") { d = Math.abs(pos.top - guide.y); if (d < distH) { distH = d; guideH = guide; offsetH = 0; } d = Math.abs(pos.top - guide.y + h); if (d < distH) { distH = d; guideH = guide; offsetH = h; } } if (guide.type === "v") { d = Math.abs(pos.left - guide.x); if (d < distV) { distV = d; guideV = guide; offsetV = 0; } d = Math.abs(pos.left - guide.x + w); if (d < distV) { distV = d; guideV = guide; offsetV = w; } } }); if (distH <= this.MIN_DISTANCE) { $("#guide-h").css("top", guideH.y - this.absoluteY).show(); if (customShape.parent.family !== 'Canvas') { ui.position.top = guideH.y - offsetH - customShape.getParent().getAbsoluteY(); } else { ui.position.top = guideH.y - offsetH; } } else { $("#guide-h").hide(); } if (distV <= this.MIN_DISTANCE) { $("#guide-v").css("left", guideV.x - this.absoluteX).show(); if (customShape.parent.family !== 'Canvas') { ui.position.left = guideV.x - offsetV - customShape.getParent().getAbsoluteX(); } else { ui.position.left = guideV.x - offsetV; } } else { $("#guide-v").hide(); } return this; }; /** * Fires the {@link PMUI.draw.Canvas#event-changeelement} event, and elaborates the structure of the object that will * be passed to the handlers, the structure contains the following fields (considering old values and new values): * * - x * - y * - parent (the shape that is parent of this shape) * - state (of the connection) * * @param {PMUI.draw.Port} port The port updated * @chainable */ PMCanvas.prototype.triggerPortChangeEvent = function (port) { var direction = port.connection.srcPort.getID() === port.getID() ? "src" : "dest", map = { src: { x: "x1", y: "y1", parent: "element_origin", type: 'element_origin_type' }, dest: { x: "x2", y: "y2", parent: "element_dest", type: 'element_dest_type' } }, point, state, zomeedState = [], i; state = port.connection.getPoints(); for (i = 0; i < state.length; i += 1) { point = port.connection.points[i]; zomeedState.push(new PMUI.util.Point(point.x / this.zoomFactor, point.y / this.zoomFactor)); } point = direction === "src" ? zomeedState[0] : zomeedState[state.length - 1]; this.updatedElement = [{ id: port.connection.getID(), type: port.connection.type, fields: [ { field: map[direction].x, oldVal: point.x, // there's no old value newVal: point.x }, { field: map[direction].y, oldVal: point.y, // there's no old value newVal: point.y }, { field: map[direction].parent, oldVal: (port.getOldParent()) ? port.getOldParent().getID() : null, newVal: port.getParent().getID() }, { field: map[direction].type, oldVal: port.connection.getNativeType(port.getParent()).type, newVal: port.connection.getNativeType(port.getParent()).type }, { field: "state", oldVal: port.connection.getOldPoints(), newVal: zomeedState }, { field: "condition", oldVal: "", newVal: port.connection.getFlowCondition() } ], relatedObject: port }]; $(this.html).trigger('changeelement'); }; /** * Attaches event listeners to this canvas, it also creates some custom triggers * used to save the data (to send it to the database later). * * The events attached to this canvas are: * * - {@link PMUI.draw.Canvas#event-mousedown Mouse down event} * - {@link PMUI.draw.Canvas#event-mousemove Mouse move event} * - {@link PMUI.draw.Canvas#event-mouseup Mouse up event} * - {@link PMUI.draw.Canvas#event-click Click event} * - {@link PMUI.draw.Canvas#event-scroll Scroll event} * * The custom events are: * * - {@link PMUI.draw.Canvas#event-createelement Create element event} * - {@link PMUI.draw.Canvas#event-removeelement Remove element event} * - {@link PMUI.draw.Canvas#event-changeelement Change element event} * - {@link PMUI.draw.Canvas#event-selectelement Select element event} * - {@link PMUI.draw.Canvas#event-rightclick Right click event} * * This method also initializes jQueryUI's droppable plugin (instantiated as `this.dropBehavior`) * @chainable */ PMCanvas.prototype.attachListeners = function () { if (this.attachedListeners === false) { var $canvas = $(this.html).click(this.onClick(this)), $canvasContainer = $canvas.parent(); $canvas.dblclick(this.onDblClick(this)); $canvas.mousedown(this.onMouseDown(this)); $canvasContainer.scroll(this.onScroll(this, $canvasContainer)); if (!this.readOnly) { $canvas.mousemove(this.onMouseMove(this)); $canvas.mouseup(this.onMouseUp(this)); //$canvas.mouseup(this.onMouseLeave(this)); } $canvas.on("createelement", this.onCreateElement(this)); $canvas.on("removeelement", this.onRemoveElement(this)); $canvas.on("changeelement", this.onChangeElement(this)); $canvas.on("selectelement", this.onSelectElement(this)); $canvas.on("rightclick", this.onRightClick(this)); $canvas.on("contextmenu", function (e) { e.preventDefault(); }); this.updateBehaviors(); this.attachedListeners = true; } return this; }; /** * enpty current selection extended * @returns {PMCanvas} */ PMCanvas.prototype.emptyCurrentSelection = function () { PMUI.draw.Canvas.prototype.emptyCurrentSelection.call(this); this.hideAllCoronas(); return this; }; /** * mouse move custom behavior * @param canvas * @returns {Function} */ PMCanvas.prototype.onMouseMove = function (canvas) { return function (e, ui) { if (canvas.lassoEnabled && canvas.isMouseDown && !canvas.rightClick) { canvas.isMouseDownAndMove = true; var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(), y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY(), topLeftX, topLeftY, bottomRightX, bottomRightY; topLeftX = Math.min(x, canvas.multipleSelectionHelper.oldX); topLeftY = Math.min(y, canvas.multipleSelectionHelper.oldY); bottomRightX = Math.max(x, canvas.multipleSelectionHelper.oldX); bottomRightY = Math.max(y, canvas.multipleSelectionHelper.oldY); canvas.multipleSelectionHelper.setPosition( topLeftX / canvas.zoomFactor, topLeftY / canvas.zoomFactor ); canvas.multipleSelectionHelper.setDimension( (bottomRightX - topLeftX) / canvas.zoomFactor, (bottomRightY - topLeftY) / canvas.zoomFactor ); } else if (canvas.canConnect) { canvas.connectHelper(e) canvas.connectStartShape.corona.hide(); canvas.hideAllFocusedLabels(); } else if (canvas.canCreateShape) { canvas.createShapeHelper(e); } }; }; /** * on mouse up behavior * @param canvas * @returns {Function} */ PMCanvas.prototype.onMouseUp = function (canvas) { return function (e, ui) { var realPoint, x, y; e.preventDefault(); if (canvas.canCreateShape) { canvas.manualCreateShape(canvas, e); canvas.canCreateShape = false; return true; } if (canvas.isMouseDownAndMove) { realPoint = canvas.relativePoint(e); x = realPoint.x; y = realPoint.y; canvas.multipleSelectionHelper.setPosition( Math.min(x, canvas.multipleSelectionHelper.zoomX) / canvas.zoomFactor, Math.min(y, canvas.multipleSelectionHelper.zoomY) / canvas.zoomFactor ); if (canvas.multipleSelectionHelper) { canvas.multipleSelectionHelper.wrapElements(); canvas.IncreaseAllConnectionZIndex(); } } else { if (!canvas.multipleSelectionHelper.wasDragged) { canvas.multipleSelectionHelper.reset().setVisible(false); } if (canvas.isMouseDown) { canvas.onClickHandler(canvas, x, y); } } canvas.isMouseDown = false; canvas.isMouseDownAndMove = false; canvas.rightClick = false; //hide lasso tool $('.mafe-toolbar-lasso').css('background-color', 'rgb(233, 233, 233)'); canvas.lassoEnabled = false; }; }; /** * Increacess z.Index to all connections * @constructor */ PMCanvas.prototype.IncreaseAllConnectionZIndex = function () { var i, connection; for (i = 0; i < this.sharedConnections.getSize(); i += 1) { connection = this.sharedConnections.get(i); connection.increaseZIndex(); } }; PMCanvas.prototype.createShapeHelper = function (e) { var realPoint = this.relativePoint(e); if (this.shapeHelper) { //remove the connection segment in order to create another one $(this.shapeHelper.html).remove(); } this.shapeHelper = new CreateShapeHelper({ x: realPoint.x * this.zoomFactor - this.getX(), y: realPoint.y * this.zoomFactor - this.getY(), parent: this, zOrder: 999, className: this.canCreateShapeClass }); this.shapeHelper.paint(); }; PMCanvas.prototype.connectHelper = function (e) { var endPoint = {}, realPoint, diff; if (this.canConnect) { if (this.connectionSegment) { //remove the connection segment in order to create another one $(this.connectionSegment.getHTML()).remove(); } //start point this.startConnectionPoint = { x: this.connectStartShape.getAbsoluteX() + this.connectStartShape.xMidPoints[1], y: this.connectStartShape.getAbsoluteY() + this.connectStartShape.yMidPoints[1] }; //Determine the point where the mouse currently is realPoint = this.relativePoint(e); endPoint.x = realPoint.x * this.zoomFactor - this.getX(); endPoint.y = realPoint.y * this.zoomFactor - this.getY(); endPoint.x += (endPoint.x - this.startConnectionPoint.x > 0) ? -5 : 5; endPoint.y += (endPoint.y - this.startConnectionPoint.y > 0) ? -5 : 5; //creates a new segment from where the helper was created to the // currently mouse location this.connectionSegment = new PMUI.draw.Segment({ startPoint: this.startConnectionPoint, endPoint: endPoint, parent: this, zOrder: 9 }); this.connectionSegment.paint(); } }; PMCanvas.prototype.connectProcedure = function (customShape, e) { var endPoint, tempPoint, initPoint, i, endPort, sourcePort, distance = 99999999, connection, endPoint2, validationResult; if (customShape.canvas.connectionSegment) { //remove the connection segment left $(customShape.canvas.connectionSegment.getHTML()).remove(); } customShape.canvas.canConnect = false; $('body').css('cursor', 'default'); validationResult = PMDesigner.connectValidator.isValid(customShape.canvas.connectStartShape, customShape); if (!validationResult.result) { //show invalid message PMDesigner.msgFlash(validationResult.msg, document.body, 'info', 3000, 5); return false; } sourcePort = new PMUI.draw.Port({ width: 10, height: 10 }); endPort = new PMUI.draw.Port({ width: 10, height: 10 }); endPoint = new PMUI.util.Point( e.pageX - customShape.canvas.getX() - customShape.getAbsoluteX() + customShape.canvas.getLeftScroll(), e.pageY - customShape.canvas.getY() - customShape.getAbsoluteY() + customShape.canvas.getTopScroll() ); endPoint2 = new PMUI.util.Point( e.pageX - customShape.canvas.getX() + customShape.canvas.getLeftScroll(), e.pageY - customShape.canvas.getY() + customShape.canvas.getTopScroll() ); for (i = 0; i < customShape.canvas.connectStartShape.xMidPoints.length; i += 1) { tempPoint = new PMUI.util.Point( customShape.canvas.connectStartShape.getAbsoluteX() + customShape.canvas.connectStartShape.xMidPoints[i], customShape.canvas.connectStartShape.getAbsoluteY() + customShape.canvas.connectStartShape.yMidPoints[i] ); if (distance > tempPoint.getSquaredDistance(endPoint2)) { distance = tempPoint.getSquaredDistance(endPoint2); initPoint = new PMUI.util.Point( customShape.canvas.connectStartShape.xMidPoints[i], customShape.canvas.connectStartShape.yMidPoints[i] ) } } customShape.canvas.connectStartShape.addPort(sourcePort, initPoint.x, initPoint.y); customShape.addPort(endPort, endPoint.x, endPoint.y, false, sourcePort); //add ports to the canvas array for regularShapes //create the connection connection = new PMFlow({ srcPort: sourcePort, destPort: endPort, segmentColor: new PMUI.util.Color(0, 0, 0), name: " ", canvas: customShape.canvas, segmentStyle: customShape.connectionType.segmentStyle, flo_type: customShape.connectionType.type }); connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({ width: 11, height: 11, canvas: customShape.canvas, decoratorPrefix: (typeof customShape.connectionType.srcDecorator !== 'undefined' && customShape.connectionType.srcDecorator !== null) ? customShape.connectionType.srcDecorator : "mafe-decorator", decoratorType: "source", parent: connection })); connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({ width: 11, height: 11, canvas: customShape.canvas, decoratorPrefix: (typeof customShape.connectionType.destDecorator !== 'undefined' && customShape.connectionType.destDecorator !== null) ? customShape.connectionType.destDecorator : "mafe-decorator", decoratorType: "target", parent: connection })); connection.canvas.commandStack.add(new PMUI.command.CommandConnect(connection)); //connect the two ports connection.connect(); connection.setSegmentMoveHandlers(); //add the connection to the canvas, that means insert its html to // the DOM and adding it to the connections array customShape.canvas.addConnection(connection); // Filling PMFlow fields connection.setTargetShape(endPort.parent); connection.setOriginShape(sourcePort.parent); // now that the connection was drawn try to create the intersections connection.checkAndCreateIntersectionsWithAll(); //attaching port listeners sourcePort.attachListeners(sourcePort); endPort.attachListeners(endPort); // finally trigger createEvent customShape.canvas.triggerCreateEvent(connection, []); }; /** * hides all corona shape */ PMCanvas.prototype.hideAllCoronas = function () { var i, shape; for (i = 0; i < this.currentSelection.getSize(); i += 1) { shape = this.currentSelection.get(i); if (shape.corona) { shape.corona.hide(); } } return this; }; /** * cancel connection action */ PMCanvas.prototype.cancelConnect = function () { if (this.connectionSegment) { $(this.connectionSegment.getHTML()).remove(); } this.canConnect = false; $('body').css('cursor', 'default'); }; /** * doble click mouse behavior * @param canvas * @returns {Function} */ PMCanvas.prototype.onDblClick = function (canvas) { return function (e, ui) { var currentLabel = canvas.currentLabel, figure, realPoint, realPoint, oldConnection; e.stopPropagation(); e.preventDefault(); realPoint = canvas.relativePoint(e); realPoint.x = realPoint.x * canvas.zoomFactor - canvas.getX(); realPoint.y = realPoint.y * canvas.zoomFactor - canvas.getY(); figure = canvas.getBestConnecion(realPoint); if (figure !== null) { canvas.emptyCurrentSelection(); figure.label.getFocus(); } }; }; PMCanvas.prototype.hideAllFocusedLabels = function () { if (this.currentLabel != null) this.currentLabel.loseFocus(); return true; }; /** * @event mousedown * MouseDown Handler of the canvas. It does the following: * * - Trigger the {@link PMUI.draw.Canvas#event-rightclick Right Click event} if it detects a right click * - Empties `canvas.currentSelection` * - Hides `canvas.currentConnection` if there's one * - Resets the position of `canvas.multipleSelectionContainer` making it visible and setting its * `[x, y]` to the point where the user did mouse down in the `canvas`. * * @param {PMUI.draw.Canvas} canvas */ PMCanvas.prototype.onMouseDown = function (canvas) { return function (e, ui) { var x = e.pageX - canvas.getX() + canvas.getLeftScroll() - canvas.getAbsoluteX(), y = e.pageY - canvas.getY() + canvas.getTopScroll() - canvas.getAbsoluteY(); if (canvas.canConnect) { canvas.cancelConnect(); } //hide corona if (canvas.coronaShape) { canvas.hideAllCoronas(); } e.preventDefault(); if (e.which === 3) { canvas.rightClick = true; $(canvas.html).trigger("rightclick", [e, canvas]); } canvas.isMouseDown = true; canvas.isMouseDownAndMove = false; // do not create the rectangle selection if a segment handler // is being dragged if (canvas.draggingASegmentHandler) { return; } // clear old selection canvas.emptyCurrentSelection(); //verify lasso is enabled if (canvas.lassoEnabled) { // hide the currentConnection if there's one canvas.hideCurrentConnection(); canvas.multipleSelectionHelper.reset(); canvas.multipleSelectionHelper.setPosition(x / canvas.zoomFactor, y / canvas.zoomFactor); canvas.multipleSelectionHelper.oldX = x; canvas.multipleSelectionHelper.oldY = y; canvas.multipleSelectionHelper.setVisible(true); canvas.multipleSelectionHelper.changeOpacity(0.2); } }; }; PMCanvas.prototype.onClick = function (canvas) { return function (e, ui) { var currentLabel = canvas.currentLabel, figure, realPoint, realPoint, oldConnection; if (currentLabel) { currentLabel.loseFocus(); $(currentLabel.textField).focusout(); } realPoint = canvas.relativePoint(e); realPoint.x = realPoint.x * canvas.zoomFactor - canvas.getX(); realPoint.y = realPoint.y * canvas.zoomFactor - canvas.getY(); figure = canvas.getBestConnecion(realPoint); canvas.hideDropConnectHandlers(); if (figure !== null && !canvas.isMouseDown) { oldConnection = canvas.currentConnection; canvas.emptyCurrentSelection(); if (oldConnection) { oldConnection.hidePortsAndHandlers(); } figure.showPortsAndHandlers(); canvas.currentConnection = figure; } }; }; PMCanvas.prototype.onMouseLeave = function (canvas) { return function (e, ui) { if (parseInt(e.screenX + 10, 10) >= parseInt(document.body.clientWidth, 10)) { window.scrollBy(1, 0); } if (parseInt(e.screenY - 75, 10) >= parseInt(document.body.clientHeight, 10)) { window.scrollBy(0, 1); } }; }; PMCanvas.prototype.manualCreateShape = function (parent, e) { var customShape = this.shapeFactory(this.canCreateShapeType), command, endPoint = {}, realPoint = this.relativePoint(e); endPoint.x = realPoint.x * this.zoomFactor - parent.getAbsoluteX(); endPoint.y = realPoint.y * this.zoomFactor - parent.getAbsoluteY(); endPoint.y -= this.getY(); parent.addElement(customShape, endPoint.x, endPoint.y, false); this.updatedElement = customShape; customShape.canvas.emptyCurrentSelection(); this.addToList(customShape); customShape.showOrHideResizeHandlers(false); if (customShape.getParent() instanceof PMLane) { command = new PMCommandCreateInLane(customShape); } else { command = new PMUI.command.CommandCreate(customShape); } this.commandStack.add(command); command.execute(); this.addToSelection(customShape); customShape.corona.show(); e.pageY += customShape.getZoomHeight() / 2; this.connectProcedure(customShape, e); this.canCreateShape = false; this.connectStartShape.corona.hide(); if (this.shapeHelper) { //remove the connection segment in order to create another one $(this.shapeHelper.html).remove(); } if (customShape.getType() === 'PMGateway' || customShape.getType() === 'PMEvent') { customShape.manualCreateMenu(e); customShape.canvas.hideAllCoronas(); } }; /** * Parses `options` creating shapes and connections and placing them in this canvas. * It does the following: * * - Creates each shape (in the same order as it is in the array `options.shapes`) * - Creates each connection (in the same order as it is in the array `options.connections`) * - Creates the an instance of {@link PMUI.command.CommandPaste} (if possible) * * @param {Object} options * @param {Array} [options.shapes=[]] The config options of each shape to be placed in this canvas. * @param {Array} [options.connections=[]] The config options of each connection to be placed in this canvas. * @param {boolean} [options.uniqueID=false] If set to true, it'll assign a unique ID to each shape created. * @param {boolean} [options.selectAfterFinish=false] If set to true, it'll add the shapes that are * direct children of this canvas to `this.currentSelection` arrayList. * @param {string} [options.prependMessage=""] The message to be prepended to each shape's label. * @param {boolean} [options.createCommand=true] If set to true it'll create a command for each creation * of a shape and connection (see {@link PMUI.command.CommandCreate}, {@link PMUI.command.CommandConnect}) and save them in * a {@link PMUI.command.CommandPaste} (for undo-redo purposes). * @param {number} [options.diffX=0] The number of pixels on the x-coordinate to move the shape on creation * @param {number} [options.diffY=0] The number of pixels on the y-coordinate to move the shape on creation * @chainable */ PMCanvas.prototype.parse = function (options) { var defaults = { shapes: [], connections: [], uniqueID: false, selectAfterFinish: false, prependMessage: "", createCommand: true, diffX: 0, diffY: 0 }, i, j, id, oldID, shape, points, shapeOptions, connection, connectionOptions, sourcePort, sourcePortOptions, sourceShape, sourceBorder, destPort, destPortOptions, destShape, destBorder, command, diffX, diffY, stackCommandCreate = [], stackCommandConnect = [], canvasID = this.getID(), mapOldId = {}, map = {}; $.extend(true, defaults, options); // set the differentials (if the shapes are pasted in the canvas) diffX = defaults.diffX; diffY = defaults.diffY; // map the canvas map[canvasID] = this; mapOldId[canvasID] = canvasID; // empty the current selection and sharedConnections as a consequence // (so that the copy is selected after) if (defaults.selectAfterFinish) { this.emptyCurrentSelection(); } for (i = 0; i < defaults.shapes.length; i += 1) { shapeOptions = {}; $.extend(true, shapeOptions, defaults.shapes[i]); // set the canvas of <shape> shapeOptions.canvas = this; // create a map of the current id with a new id oldID = shapeOptions.id; // generate a unique id on user request if (defaults.uniqueID) { shapeOptions.id = PMUI.generateUniqueId(); } mapOldId[oldID] = shapeOptions.id; // change labels' messages (using prependMessage) if (shapeOptions.labels) { for (j = 0; j < shapeOptions.labels.length; j += 1) { shapeOptions.labels[j].message = defaults.prependMessage + shapeOptions.labels[j].message; } } // create an instance of the shape based on its type shape = this.shapeFactory(shapeOptions.extendedType, shapeOptions); // map the instance with its id map[shapeOptions.id] = shape; // if the shapes don't have a valid parent then set the parent // to be equal to the canvas // TODO: ADD shapeOptions.topLeftOnCreation TO EACH SHAPE if (!mapOldId[shapeOptions.parent]) { this.addElement(shape, shapeOptions.x + diffX, shapeOptions.y + diffY, true); } else if (shapeOptions.parent !== canvasID) { // get the parent of this shape map[mapOldId[shapeOptions.parent]].addElement(shape, shapeOptions.x, shapeOptions.y, true); } else { // move the shapes a little (so it can be seen that // they were duplicated) map[mapOldId[shapeOptions.parent]].addElement(shape, shapeOptions.x + diffX, shapeOptions.y + diffY, true); } // perform some extra actions defined for each shape shape.parseHook(); shape.attachListeners(); // execute command create but don't add it to the canvas.commandStack command = new PMUI.command.CommandCreate(shape); command.execute(); stackCommandCreate.push(command); } for (i = 0; i < defaults.connections.length; i += 1) { connectionOptions = {}; $.extend(true, connectionOptions, defaults.connections[i]); // state of the connection points = connectionOptions.state || []; // determine the shapes sourcePortOptions = connectionOptions.srcPort; sourceShape = map[mapOldId[sourcePortOptions.parent]]; sourceBorder = sourceShape.getBorderConsideringLayers(); destPortOptions = connectionOptions.destPort; destShape = map[mapOldId[destPortOptions.parent]]; destBorder = destShape.getBorderConsideringLayers(); // populate points if points has no info (backwards compatibility, // e.g. the flow state is null) if (points.length === 0) { points.push({ x: sourcePortOptions.x + sourceShape.getAbsoluteX(), y: sourcePortOptions.y + sourceShape.getAbsoluteY() }); points.push({ x: destPortOptions.x + destShape.getAbsoluteX(), y: destPortOptions.y + destShape.getAbsoluteY() }); } //create the ports sourcePort = new PMUI.draw.Port({ width: 8, height: 8 }); destPort = new PMUI.draw.Port({ width: 8, height: 8 }); // add the ports to the shapes // LOGIC: points is an array of points relative to the canvas. // CustomShape.addPort() requires that the point passed as an argument // is respect to the shape, so transform the point's coordinates (also // consider the border) sourceShape.addPort( sourcePort, points[0].x + diffX + sourceBorder - sourceShape.getAbsoluteX(), points[0].y + diffX + sourceBorder - sourceShape.getAbsoluteY() ); destShape.addPort( destPort, points[points.length - 1].x + diffX + destBorder - destShape.getAbsoluteX(), points[points.length - 1].y + diffY + destBorder - destShape.getAbsoluteY(), false, sourcePort ); connection = this.connectionFactory( connectionOptions.type, { srcPort: sourcePort, destPort: destPort, segmentColor: new PMUI.util.Color(92, 156, 204), canvas: this, segmentStyle: connectionOptions.segmentStyle } ); connection.id = connectionOptions.id || PMUI.generateUniqueId(); if (defaults.uniqueID) { connection.id = PMUI.generateUniqueId(); } //set its decorators connection.setSrcDecorator(new PMUI.draw.ConnectionDecorator({ width: 1, height: 1, canvas: this, decoratorPrefix: connectionOptions.srcDecoratorPrefix, decoratorType: "source", parent: connection })); connection.setDestDecorator(new PMUI.draw.ConnectionDecorator({ width: 1, height: 1, canvas: this, decoratorPrefix: connectionOptions.destDecoratorPrefix, decoratorType: "target", parent: connection })); command = new PMUI.command.CommandConnect(connection); stackCommandConnect.push(command); // connect the two ports if (points.length >= 3) { connection.connect({ algorithm: 'user', points: connectionOptions.state, dx: defaults.diffX, dy: defaults.diffY }); } else { connection.connect(); } connection.setSegmentMoveHandlers(); // add the connection to the canvas, that means insert its html to // the DOM and adding it to the connections array this.addConnection(connection); // now that the connection was drawn try to create the intersections connection.checkAndCreateIntersectionsWithAll(); //attaching port listeners sourcePort.attachListeners(sourcePort); destPort.attachListeners(destPort); this.triggerCreateEvent(connection, []); } // finally add to currentSelection each shape if possible (this method is // down here because of the zIndex problem with connections) if (defaults.selectAfterFinish) { for (id in map) { if (map.hasOwnProperty(id)) { if (map[id].family !== 'Canvas') { this.addToSelection(map[id]); } } } } // create command if possible if (defaults.createCommand) { this.commandStack.add(new PMUI.command.CommandPaste(this, { stackCommandCreate: stackCommandCreate, stackCommandConnect: stackCommandConnect })); } return this; }; /** * Fires the {@link PMUI.draw.Canvas#event-removeelement} event, and elaborates the structure of the object that will * be passed to the handlers. * @param {PMUI.draw.CustomShape} shape The shape created * @param {Array} relatedElements The array with the other elements created * @chainable */ PMCanvas.prototype.triggerRemoveEvent = function (shape, relatedElements) { if (relatedElements.length === 0) { if (shape) { relatedElements.push(shape); } } this.updatedElement = { id: (shape && shape.id) || null, type: (shape && shape.type) || null, relatedObject: shape, relatedElements: relatedElements }; this.canvas.hideDragConnectHandlers(); if (shape && shape.validatorMarker) { shape.validatorMarker.removeBoxMarker(); } $(this.html).trigger('removeelement'); return this; }; PMCanvas.prototype.createConnectHandlers = function (resizableStyle, nonResizableStyle) { var i, number = 20, connectHandler; //add the rest to the mid list for (i = 0; i < number; i += 1) { connectHandler = new PMConnectHandler({ parent: this, zOrder: PMUI.util.Style.MAX_ZINDEX + 4, representation: new PMUI.draw.Rectangle(), resizableStyle: resizableStyle, nonResizableStyle: nonResizableStyle }); this.dragConnectHandlers.insert( connectHandler ); if (!this.html) { return; } this.html.appendChild(connectHandler.getHTML()); connectHandler.setPosition(100, 100); connectHandler.setCategory("dragConnectHandler"); connectHandler.attachListeners(); connectHandler.paint(); } for (i = 0; i < number; i += 1) { connectHandler = new PMConnectHandler({ parent: this, zOrder: PMUI.util.Style.MAX_ZINDEX + 1, representation: new PMUI.draw.Rectangle(), resizableStyle: resizableStyle, nonResizableStyle: nonResizableStyle }); this.dropConnectHandlers.insert( connectHandler ); if (!this.html) { return; } this.html.appendChild(connectHandler.getHTML()); connectHandler.setPosition(400, 100); connectHandler.setCategory("dropConnectHandler"); connectHandler.attachListeners(); connectHandler.paint(); } return this; }; PMCanvas.prototype.hideDragConnectHandlers = function () { var connectHandler, i; for (i = 0; i < this.dragConnectHandlers.getSize(); i += 1) { connectHandler = this.dragConnectHandlers.get(i); connectHandler.setVisible(false); } return this; }; PMCanvas.prototype.hideDropConnectHandlers = function () { var connectHandler, i; for (i = 0; i < this.dropConnectHandlers.getSize(); i += 1) { connectHandler = this.dropConnectHandlers.get(i); connectHandler.setVisible(false); } return this; }; PMCanvas.prototype.applyZoom = function (scale) { this.hideDragConnectHandlers(); this.hideDropConnectHandlers(); PMUI.draw.Canvas.prototype.applyZoom.call(this, scale); return this; }; PMCanvas.prototype.existThatName = function (element, name) { var i, shape, result = false; for (i = 0; i < this.customShapes.getSize(); i += 1) { shape = this.customShapes.get(i); if (shape.getID() !== element.getID() && shape.getName() === element.getName()) { result = true; break; } } return result; }; PMCanvas.prototype.validateName = function (element, newText, oldText) { var result = true; if ((typeof newText === "string") && (newText.trim() === "")) { result = false; PMDesigner.msgFlash("Task/sub-process name can't be empty".translate(), document.body, 'error', 3000, 5); } else if (this.existThatName(element.parent, newText)) { result = false; PMDesigner.msgFlash('This name already exists.'.translate(), document.body, 'error', 3000, 5); } return result; }; PMCanvas.prototype.addConnection = function (conn) { var shapeElement; PMUI.draw.Canvas.prototype.addConnection.call(this, conn); if (conn.fl