/** * $Id: Graph.js,v 1.33 2012-05-28 15:38:45 gaudenz Exp $ * Copyright (c) 2006-2012, JGraph Ltd */ /** * Constructs a new graph instance. Note that the constructor does not take a * container because the graph instance is needed for creating the UI, which * in turn will create the container for the graph. Hence, the container is * assigned later in EditorUi. */ Graph = function(container, model, renderHint, stylesheet) { mxGraph.call(this, container, model, renderHint, stylesheet); this.setConnectable(true); this.setDropEnabled(true); this.setPanning(true); this.setTooltips(!mxClient.IS_TOUCH); this.setAllowLoops(true); this.allowAutoPanning = true; this.connectionHandler.setCreateTarget(true); // Sets the style to be used when an elbow edge is double clicked this.alternateEdgeStyle = 'vertical'; this.loadStylesheet(); // Creates rubberband selection var rubberband = new mxRubberband(this); this.getRubberband = function() { return rubberband; }; // Adds support for HTML labels via style. Note: Currently, only the Java // backend supports HTML labels but CSS support is limited to the following: // http://docs.oracle.com/javase/6/docs/api/index.html?javax/swing/text/html/CSS.html this.isHtmlLabel = function(cell) { var state = this.view.getState(cell); var style = (state != null) ? state.style : this.getCellStyle(cell); return style['html'] == '1'; }; // Unlocks all cells this.isCellLocked = function(cell) { return false; }; // Tap and hold brings up context menu. // Tolerance slightly below graph tolerance is better. this.connectionHandler.tapAndHoldTolerance = 16; // Tap and hold on background starts rubberband on cell starts connecting var connectionHandlerTapAndHold = this.connectionHandler.tapAndHold; this.connectionHandler.tapAndHold = function(me, state) { if (state == null) { if (!this.graph.panningHandler.active) { rubberband.start(me.getGraphX(), me.getGraphY()); this.graph.panningHandler.panningTrigger = false; } } else if (tapAndHoldStartsConnection) { connectionHandlerTapAndHold.apply(this, arguments); } else if (this.graph.isCellSelected(state.cell) && this.graph.getSelectionCount() > 1) { this.graph.removeSelectionCell(state.cell); } }; if (touchStyle) { this.initTouch(); } }; // Graph inherits from mxGraph mxUtils.extend(Graph, mxGraph); /** * Loads the stylesheet for this graph. */ Graph.prototype.loadStylesheet = function() { var node = mxUtils.load(STYLE_PATH + '/default.xml').getDocumentElement(); var dec = new mxCodec(node.ownerDocument); dec.decode(node, this.getStylesheet()); }; /** * Inverts the elbow edge style without removing existing styles. */ Graph.prototype.flipEdge = function(edge) { if (edge != null) { var state = this.view.getState(edge); var style = (state != null) ? state.style : this.getCellStyle(edge); if (style != null) { var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW, mxConstants.ELBOW_HORIZONTAL); var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ? mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL; this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]); } } }; /** * Disables folding for non-swimlanes. */ Graph.prototype.isCellFoldable = function(cell) { return this.foldingEnabled && this.isSwimlane(cell); }; /** * Disables drill-down for non-swimlanes. */ Graph.prototype.isValidRoot = function(cell) { return this.isSwimlane(cell); }; /** * Overrides createGroupCell to set the group style for new groups to 'group'. */ Graph.prototype.createGroupCell = function() { var group = mxGraph.prototype.createGroupCell.apply(this, arguments); group.setStyle('group'); return group; }; /** * Overrides tooltips to show position and size */ Graph.prototype.getTooltipForCell = function(cell) { var tip = ''; if (this.getModel().isVertex(cell)) { var geo = this.getCellGeometry(cell); var f2 = function(x) { return Math.round(parseFloat(x) * 100) / 100; }; if (geo != null) { if (tip == null) { tip = ''; } else if (tip.length > 0) { tip += '\n'; } tip += 'X: ' + f2(geo.x) + '\nY: ' + f2(geo.y) + '\nW: ' + f2(geo.width) + '\nH: ' + f2(geo.height); } } else if (this.getModel().isEdge(cell)) { tip = mxGraph.prototype.getTooltipForCell.apply(this, arguments); } return tip; }; /** * Customized graph for touch devices. */ Graph.prototype.initTouch = function() { // Disables new connections via "hotspot" this.connectionHandler.marker.isEnabled = function() { return this.graph.connectionHandler.first != null; }; // Hides menu when editing starts this.addListener(mxEvent.START_EDITING, function(sender, evt) { this.panningHandler.hideMenu(); }); // Context menu for touchstyle var showMenu = false; var menuCell = null; // Checks if native hit detection did not return anything and does custom // hit detection for edges to take into account the tolerance this.updateMouseEvent = function(me) { mxGraph.prototype.updateMouseEvent.apply(this, arguments); if (me.getState() == null) { var cell = this.getCellAt(me.graphX, me.graphY); if (this.getModel().isEdge(cell)) { me.state = this.view.getState(cell); if (me.state != null && me.state.shape != null) { this.container.style.cursor = me.state.shape.node.style.cursor; } } } if (me.getState() == null) { this.container.style.cursor = 'default'; } }; // Handles popup menu on touch devices (tap selected cell) this.fireMouseEvent = function(evtName, me, sender) { if (evtName == mxEvent.MOUSE_DOWN) { if (!this.panningHandler.isMenuShowing()) { menuCell = me.getCell(); showMenu = (menuCell != null) ? this.isCellSelected(menuCell) : this.isSelectionEmpty(); } else { showMenu = false; menuCell = null; } } else if (evtName == mxEvent.MOUSE_UP) { if (showMenu && !this.isEditing()) { if (!this.panningHandler.isMenuShowing()) { var x = mxEvent.getClientX(me.getEvent()); var y = mxEvent.getClientY(me.getEvent()); this.panningHandler.popup(x + 16, y, menuCell, me.getEvent()); } showMenu = false; menuCell = null; me.consume(); return; } showMenu = false; menuCell = null; } mxGraph.prototype.fireMouseEvent.apply(this, arguments); if (evtName == mxEvent.MOUSE_MOVE && me.isConsumed()) { showMenu = false; menuCell = null; } }; }; /** * Registers shapes. */ (function() { // Touch-specific static overrides if (touchStyle) { // Sets constants for touch style mxConstants.HANDLE_SIZE = 16; mxConstants.LABEL_HANDLE_SIZE = 7; // Larger tolerance and grid for real touch devices if (mxClient.IS_TOUCH) { mxVertexHandler.prototype.tolerance = 4; mxEdgeHandler.prototype.tolerance = 6; Graph.prototype.tolerance = 14; Graph.prototype.gridSize = 20; // One finger pans (no rubberband selection) must start regardless of mouse button mxPanningHandler.prototype.selectOnPopup = false; mxPanningHandler.prototype.useLeftButtonForPanning = true; mxPanningHandler.prototype.isPanningTrigger = function(me) { var evt = me.getEvent(); return (this.useLeftButtonForPanning && (this.ignoreCell || me.getState() == null)/* && mxEvent.isLeftMouseButton(evt)*/) || (mxEvent.isControlDown(evt) && mxEvent.isShiftDown(evt)) || (this.usePopupTrigger && mxEvent.isPopupTrigger(evt)); }; } // Don't clear selection if multiple cells selected var graphHandlerMouseDown = mxGraphHandler.prototype.mouseDown; mxGraphHandler.prototype.mouseDown = function(sender, me) { graphHandlerMouseDown.apply(this, arguments); if (this.graph.isCellSelected(me.getCell()) && this.graph.getSelectionCount() > 1) { this.delayedSelection = false; } }; // Changes order of panninghandler Graph.prototype.createHandlers = function(container) { this.tooltipHandler = new mxTooltipHandler(this); this.tooltipHandler.setEnabled(false); // Selection cells first this.selectionCellsHandler = new mxSelectionCellsHandler(this); this.panningHandler = new mxPanningHandler(this); this.panningHandler.panningEnabled = false; this.connectionHandler = new mxConnectionHandler(this); this.connectionHandler.setEnabled(false); this.graphHandler = new mxGraphHandler(this); }; // On connect the target is selected and we clone the cell of the preview edge for insert mxConnectionHandler.prototype.selectCells = function(edge, target) { if (touchStyle && target != null) { this.graph.setSelectionCell(target); } else { this.graph.setSelectionCell(edge); } }; // Overrides double click handling to use the tolerance // FIXME: Double click on edges in iPad needs focus on textarea var graphDblClick = mxGraph.prototype.dblClick; Graph.prototype.dblClick = function(evt, cell) { if (cell == null) { var pt = mxUtils.convertPoint(this.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt)); cell = this.getCellAt(pt.x, pt.y); } graphDblClick.call(this, evt, cell); }; // Sets the submenu icon mxPopupMenu.prototype.submenuImage = IMAGE_PATH + '/touch-submenu.png'; // Rounded edge and vertex handles var touchHandle = new mxImage(IMAGE_PATH + '/touch-handle.png', 16, 16); mxVertexHandler.prototype.handleImage = touchHandle; mxEdgeHandler.prototype.handleImage = touchHandle; mxOutline.prototype.sizerImage = touchHandle; // Pre-fetches touch handle new Image().src = touchHandle.src; // Adds connect icon to selected vertices var connectorSrc = IMAGE_PATH + '/touch-connector.png'; var vertexHandlerInit = mxVertexHandler.prototype.init; mxVertexHandler.prototype.init = function() { vertexHandlerInit.apply(this, arguments); var md = (mxClient.IS_TOUCH) ? 'touchstart' : 'mousedown'; // Only show connector image on one cell and do not show on containers if (showConnectorImg && this.graph.connectionHandler.isEnabled() && this.graph.isCellConnectable(this.state.cell) && !this.graph.isValidRoot(this.state.cell) && this.graph.getSelectionCount() == 1) { this.connectorImg = mxUtils.createImage(connectorSrc); this.connectorImg.style.cursor = 'pointer'; this.connectorImg.style.width = '29px'; this.connectorImg.style.height = '29px'; this.connectorImg.style.position = 'absolute'; if (!mxClient.IS_TOUCH) { this.connectorImg.setAttribute('title', mxResources.get('connect')); mxEvent.redirectMouseEvents(this.connectorImg, this.graph, this.state); } // Adds 2px tolerance this.connectorImg.style.padding = '2px'; // Starts connecting on touch/mouse down mxEvent.addListener(this.connectorImg, md, mxUtils.bind(this, function(evt) { this.graph.panningHandler.hideMenu(); var pt = mxUtils.convertPoint(this.graph.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt)); this.graph.connectionHandler.start(this.state, pt.x, pt.y); this.graph.isMouseDown = true; mxEvent.consume(evt); }) ); this.graph.container.appendChild(this.connectorImg); } this.redrawTools(); }; var vertexHandlerRedraw = mxVertexHandler.prototype.redraw; mxVertexHandler.prototype.redraw = function() { vertexHandlerRedraw.apply(this); this.redrawTools(); }; mxVertexHandler.prototype.redrawTools = function() { if (this.state != null && this.connectorImg != null) { // Top right for single-sizer if (mxVertexHandler.prototype.singleSizer) { this.connectorImg.style.left = (this.state.x + this.state.width - this.connectorImg.offsetWidth / 2) + 'px'; this.connectorImg.style.top = (this.state.y - this.connectorImg.offsetHeight / 2) + 'px'; } else { this.connectorImg.style.left = (this.state.x + this.state.width + mxConstants.HANDLE_SIZE / 2 + 4/* - 2 padding*/) + 'px'; this.connectorImg.style.top = (this.state.y + (this.state.height - this.connectorImg.offsetHeight) / 2) + 'px'; } } }; var vertexHandlerDestroy = mxVertexHandler.prototype.destroy; mxVertexHandler.prototype.destroy = function(sender, me) { vertexHandlerDestroy.apply(this, arguments); if (this.connectorImg != null) { this.connectorImg.parentNode.removeChild(this.connectorImg); this.connectorImg = null; } }; // Pre-fetches touch connector new Image().src = connectorSrc; } else // not touchStyle { mxConnectionHandler.prototype.connectImage = new mxImage(IMAGE_PATH + '/connector.png', 15, 15); } // Pre-fetches submenu image new Image().src = mxPopupMenu.prototype.submenuImage; // Pre-fetches connect image if (mxConnectionHandler.prototype.connectImage != null) { new Image().src = mxConnectionHandler.prototype.connectImage.src; } // Cube Shape, supports size style function CubeShape() { }; CubeShape.prototype = new mxCylinder(); CubeShape.prototype.constructor = CubeShape; CubeShape.prototype.size = 20; CubeShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var s = Math.min(w, Math.min(h, mxUtils.getValue(this.style, 'size', this.size) * this.scale)); if (isForeground) { path.moveTo(s, h); path.lineTo(s, s); path.lineTo(0, 0); path.moveTo(s, s); path.lineTo(w, s); path.end(); } else { path.moveTo(0, 0); path.lineTo(w - s, 0); path.lineTo(w, s); path.lineTo(w, h); path.lineTo(s, h); path.lineTo(0, h - s); path.lineTo(0, 0); path.close(); path.end(); } }; mxCellRenderer.prototype.defaultShapes['cube'] = CubeShape; // Note Shape, supports size style function NoteShape() { }; NoteShape.prototype = new mxCylinder(); NoteShape.prototype.constructor = NoteShape; NoteShape.prototype.size = 30; NoteShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var s = Math.min(w, Math.min(h, mxUtils.getValue(this.style, 'size', this.size) * this.scale)); if (isForeground) { path.moveTo(w - s, 0); path.lineTo(w - s, s); path.lineTo(w, s); path.end(); } else { path.moveTo(0, 0); path.lineTo(w - s, 0); path.lineTo(w, s); path.lineTo(w, h); path.lineTo(0, h); path.lineTo(0, 0); path.close(); path.end(); } }; mxCellRenderer.prototype.defaultShapes['note'] = NoteShape; // Folder Shape, supports tabWidth, tabHeight styles function FolderShape() { }; FolderShape.prototype = new mxCylinder(); FolderShape.prototype.constructor = FolderShape; FolderShape.prototype.tabWidth = 60; FolderShape.prototype.tabHeight = 20; FolderShape.prototype.tabPosition = 'right'; FolderShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var tw = mxUtils.getValue(this.style, 'tabWidth', this.tabWidth); var th = mxUtils.getValue(this.style, 'tabHeight', this.tabHeight); var tp = mxUtils.getValue(this.style, 'tabPosition', this.tabPosition); var dx = Math.min(w, tw * this.scale); var dy = Math.min(h, th * this.scale); if (isForeground) { if (tp == 'left') { path.moveTo(0, dy); path.lineTo(dx, dy); } // Right is default else { path.moveTo(w - dx, dy); path.lineTo(w, dy); } path.end(); } else { if (tp == 'left') { path.moveTo(0, 0); path.lineTo(dx, 0); path.lineTo(dx, dy); path.lineTo(w, dy); } // Right is default else { path.moveTo(0, dy); path.lineTo(w - dx, dy); path.lineTo(w - dx, 0); path.lineTo(w, 0); } path.lineTo(w, h); path.lineTo(0, h); path.lineTo(0, dy); path.close(); path.end(); } }; mxCellRenderer.prototype.defaultShapes['folder'] = FolderShape; // Card Shape, supports size style function CardShape() { }; CardShape.prototype = new mxCylinder(); CardShape.prototype.constructor = CardShape; CardShape.prototype.size = 30; CardShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var s = Math.min(w, Math.min(h, mxUtils.getValue(this.style, 'size', this.size) * this.scale)); if (!isForeground) { path.moveTo(s, 0); path.lineTo(w, 0); path.lineTo(w, h); path.lineTo(0, h); path.lineTo(0, s); path.lineTo(s, 0); path.close(); path.end(); } }; mxCellRenderer.prototype.defaultShapes['card'] = CardShape; // Tape Shape, supports size style function TapeShape() { }; TapeShape.prototype = new mxCylinder(); TapeShape.prototype.constructor = TapeShape; TapeShape.prototype.size = 0.4; TapeShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var s = mxUtils.getValue(this.style, 'size', this.size); var dy = h * s; var fy = 1.4; if (!isForeground) { path.moveTo(0, dy / 2); path.quadTo(w / 4, dy * fy, w / 2, dy / 2); path.quadTo(w * 3 / 4, dy * (1 - fy), w, dy / 2); path.lineTo(w, h - dy / 2); path.quadTo(w * 3 / 4, h - dy * fy, w / 2, h - dy / 2); path.quadTo(w / 4, h - dy * (1 - fy), 0, h - dy / 2); path.lineTo(0, dy / 2); path.close(); path.end(); } }; mxCellRenderer.prototype.defaultShapes['tape'] = TapeShape; // Tape Shape, supports size style function StepShape() { }; StepShape.prototype = new mxCylinder(); StepShape.prototype.constructor = StepShape; StepShape.prototype.size = 0.2; StepShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var s = w * mxUtils.getValue(this.style, 'size', this.size); if (!isForeground) { path.moveTo(0, 0); path.lineTo(w - s, 0); path.lineTo(w, h / 2); path.lineTo(w - s, h); path.lineTo(0, h); path.lineTo(s, h / 2); path.close(); path.end(); } }; mxCellRenderer.prototype.defaultShapes['step'] = StepShape; // Tape Shape, supports size style function PlusShape() { }; PlusShape.prototype = new mxCylinder(); PlusShape.prototype.constructor = PlusShape; PlusShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var border = Math.min(w / 5, h / 5) + 1; if (isForeground) { path.moveTo(w / 2, border); path.lineTo(w / 2, h - border); path.moveTo(border, h / 2); path.lineTo(w - border, h / 2); path.end(); } else { path.moveTo(0, 0); path.lineTo(w, 0); path.lineTo(w, h); path.lineTo(0, h); path.close(); } }; mxCellRenderer.prototype.defaultShapes['plus'] = PlusShape; // Tape Shape, supports size style function MessageShape() { }; MessageShape.prototype = new mxCylinder(); MessageShape.prototype.constructor = MessageShape; MessageShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { if (isForeground) { path.moveTo(0, 0); path.lineTo(w / 2, h / 2); path.lineTo(w, 0); path.end(); } else { path.moveTo(0, 0); path.lineTo(w, 0); path.lineTo(w, h); path.lineTo(0, h); path.close(); } }; mxCellRenderer.prototype.defaultShapes['message'] = MessageShape; // New Actor Shape function UmlActorShape() { }; UmlActorShape.prototype = new mxCylinder(); UmlActorShape.prototype.constructor = UmlActorShape; UmlActorShape.prototype.addPipe = true; UmlActorShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var width = w / 3; var height = h / 4; if (!isForeground) { path.moveTo(w / 2, height); path.curveTo(w / 2 - width, height, w / 2 - width, 0, w / 2, 0); path.curveTo(w / 2 + width, 0, w / 2 + width, height, w / 2, height); path.close(); path.moveTo(w / 2, height); path.lineTo(w / 2, 2 * h / 3); // Arms path.moveTo(w / 2, h / 3); path.lineTo(0, h / 3); path.moveTo(w / 2, h / 3); path.lineTo(w, h / 3); // Legs path.moveTo(w / 2, 2 * h / 3); path.lineTo(0, h); path.moveTo(w / 2, 2 * h / 3); path.lineTo(w, h); path.end(); } }; // Replaces existing actor shape mxCellRenderer.prototype.defaultShapes['umlActor'] = UmlActorShape; // New Actor Shape function LollipopShape() { }; LollipopShape.prototype = new mxCylinder(); LollipopShape.prototype.constructor = LollipopShape; LollipopShape.prototype.size = 10; LollipopShape.prototype.addPipe = true; LollipopShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var ss = this.scale * mxUtils.getValue(this.style, 'size', this.size); var width = ss * 2 / 3; var height = ss; if (!isForeground) { path.moveTo(w / 2, height); path.curveTo(w / 2 - width, height, w / 2 - width, 0, w / 2, 0); path.curveTo(w / 2 + width, 0, w / 2 + width, height, w / 2, height); path.close(); path.moveTo(w / 2, height); path.lineTo(w / 2, h); path.end(); } }; // Replaces existing actor shape mxCellRenderer.prototype.defaultShapes['lollipop'] = LollipopShape; // Folder Shape, supports tabWidth, tabHeight styles function ComponentShape() { }; ComponentShape.prototype = new mxCylinder(); ComponentShape.prototype.constructor = ComponentShape; ComponentShape.prototype.jettyWidth = 32; ComponentShape.prototype.jettyHeight = 12; ComponentShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) { var jw = mxUtils.getValue(this.style, 'jettyWidth', this.jettyWidth); var jh = mxUtils.getValue(this.style, 'jettyHeight', this.jettyHeight); var dx = jw * this.scale; var dy = jh * this.scale; var x0 = dx / 2; var x1 = x0 + dx / 2; var y0 = 0.3 * h - dy / 2; var y1 = 0.7 * h - dy / 2; if (isForeground) { path.moveTo(x0, y0); path.lineTo(x1, y0); path.lineTo(x1, y0 + dy); path.lineTo(x0, y0 + dy); path.moveTo(x0, y1); path.lineTo(x1, y1); path.lineTo(x1, y1 + dy); path.lineTo(x0, y1 + dy); path.end(); } else { path.moveTo(x0, 0); path.lineTo(w, 0); path.lineTo(w, h); path.lineTo(x0, h); path.lineTo(x0, y1 + dy); path.lineTo(0, y1 + dy); path.lineTo(0, y1); path.lineTo(x0, y1); path.lineTo(x0, y0 + dy); path.lineTo(0, y0 + dy); path.lineTo(0, y0); path.lineTo(x0, y0); path.close(); path.end(); } }; mxCellRenderer.prototype.defaultShapes['component'] = ComponentShape; // State Shapes derives from double ellipse function StateShape() { }; StateShape.prototype = new mxDoubleEllipse(); StateShape.prototype.constructor = StateShape; StateShape.prototype.outerStroke = true; StateShape.prototype.createSvg = function() { var g = mxDoubleEllipse.prototype.createSvg.apply(this, arguments); this.foreground.setAttribute('fill', this.innerNode.getAttribute('fill')); this.foreground.setAttribute('stroke', this.stroke); this.innerNode.setAttribute('fill', 'none'); this.innerNode.setAttribute('stroke', (this.outerStroke) ? this.stroke : 'none'); return g; }; StateShape.prototype.redrawSvg = function() { mxDoubleEllipse.prototype.redrawSvg.apply(this, arguments); // Workaround for visible background this.innerNode.setAttribute('fill', 'none'); if (this.shadowNode != null) { this.shadowNode.setAttribute('cx', this.foreground.getAttribute('cx')); this.shadowNode.setAttribute('cy', this.foreground.getAttribute('cy')); this.shadowNode.setAttribute('rx', this.foreground.getAttribute('rx')); this.shadowNode.setAttribute('ry', this.foreground.getAttribute('ry')); } }; StateShape.prototype.createVml = function() { var result = mxDoubleEllipse.prototype.createVml.apply(this, arguments); if (this.fillNode != null) { this.foreground.appendChild(this.fillNode); this.foreground.filled = 'true'; } this.background.filled = 'false'; this.background.stroked = (this.outerStroke) ? 'true' : 'false'; if (this.shadowNode != null) { this.foreground.appendChild(this.shadowNode); } return result; }; mxCellRenderer.prototype.defaultShapes['endState'] = StateShape; function StartStateShape() { }; StartStateShape.prototype = new StateShape(); StartStateShape.prototype.constructor = StartStateShape; StartStateShape.prototype.outerStroke = false; mxCellRenderer.prototype.defaultShapes['startState'] = StartStateShape; // Image export for state shapes var imageExportInitShapes = mxImageExport.prototype.initShapes; mxImageExport.prototype.initShapes = function() { imageExportInitShapes.apply(this, arguments); function createStateShape(outerStroke) { return { drawShape: function(canvas, state, bounds, background) { var x = bounds.x; var y = bounds.y; var w = bounds.width; var h = bounds.height; if (background) { var inset = Math.min(4, Math.min(w / 5, h / 5)); x += inset; y += inset; w -= 2 * inset; h -= 2 * inset; if (w > 0 && h > 0) { canvas.ellipse(x, y, w, h); } return true; } else { canvas.fillAndStroke(); if (outerStroke) { canvas.ellipse(x, y, w, h); canvas.stroke(); } } } }; }; this.shapes['endState'] = createStateShape(true); this.shapes['startState'] = createStateShape(false); }; // Custom edge shape function LinkShape() { }; LinkShape.prototype = new mxArrow(); LinkShape.prototype.constructor = LinkShape; LinkShape.prototype.enableFill = false; LinkShape.prototype.addPipe = true; LinkShape.prototype.augmentBoundingBox = function(bbox) { bbox.grow(10 * this.scale); mxShape.prototype.augmentBoundingBox.apply(this, arguments); }; LinkShape.prototype.redrawPath = function(path, x, y, w, h) { // All points are offset path.translate.x -= x; path.translate.y -= y; // Geometry of arrow var width = 10 * this.scale; // Base vector (between end points) var p0 = this.points[0]; var pe = this.points[this.points.length - 1]; var dx = pe.x - p0.x; var dy = pe.y - p0.y; var dist = Math.sqrt(dx * dx + dy * dy); var length = dist; // Computes the norm and the inverse norm var nx = dx / dist; var ny = dy / dist; var basex = length * nx; var basey = length * ny; var floorx = width * ny/3; var floory = -width * nx/3; // Computes points var p0x = p0.x - floorx / 2; var p0y = p0.y - floory / 2; var p1x = p0x + floorx; var p1y = p0y + floory; var p2x = p1x + basex; var p2y = p1y + basey; var p3x = p2x + floorx; var p3y = p2y + floory; // p4 not needed var p5x = p3x - 3 * floorx; var p5y = p3y - 3 * floory; // LATER: Add support for n points path.moveTo(p1x, p1y); path.lineTo(p2x, p2y); path.moveTo(p5x + floorx, p5y + floory); path.lineTo(p0x, p0y); path.end(); }; mxCellRenderer.prototype.defaultShapes['link'] = LinkShape; // Defines custom marker mxMarker.markers['dash'] = function(node, type, pe, nx, ny, strokewidth, size, scale, isVml) { nx = nx * (size + strokewidth); ny = ny * (size + strokewidth); if (isVml) { node.setAttribute('path', 'm' + Math.floor(pe.x - nx / 2- ny / 2) + ' ' + Math.floor(pe.y - ny / 2 + nx / 2) + ' l ' + Math.floor(pe.x + ny / 2 - 3 * nx / 2) + ' ' + Math.floor(pe.y - 3 * ny / 2 - nx / 2) + ' e'); } else { node.setAttribute('d', 'M ' + (pe.x - nx / 2 - ny / 2) + ' ' + (pe.y - ny / 2 + nx / 2) + ' L ' + (pe.x + ny / 2 - 3 * nx / 2) + ' ' + (pe.y - 3 * ny / 2 - nx / 2) + ' z'); } // Returns the offset for the edge return new mxPoint(0, 0); }; // Registers the marker in mxImageExport var mxImageExportInitMarkers = mxImageExport.prototype.initMarkers; mxImageExport.prototype.initMarkers = function() { mxImageExportInitMarkers.apply(this, arguments); this.markers['dash'] = function(canvas, state, type, pe, unitX, unitY, size, source, sw) { nx = unitX * (size + sw); ny = unitY * (size + sw); canvas.begin(); canvas.moveTo(pe.x - nx / 2 - ny / 2, pe.y - ny / 2 + nx / 2); canvas.lineTo(pe.x + ny / 2 - 3 * nx / 2, pe.y - 3 * ny / 2 - nx / 2); canvas.stroke(); // Returns the offset for the edge return new mxPoint(0, 0); }; }; // Enables crisp rendering in SVG except for connectors, actors, cylinder, // ellipses must be enabled after rendering the sidebar items mxShape.prototype.crisp = true; mxShape.prototype.roundedCrispSvg = false; mxActor.prototype.crisp = false; mxCylinder.prototype.crisp = false; mxEllipse.prototype.crisp = false; mxDoubleEllipse.prototype.crisp = false; mxConnector.prototype.crisp = false; FolderShape.prototype.crisp = true; ComponentShape.prototype.crisp = true; // Implements custom handlers var handlers = {'swimlane': mxSwimlaneHandler, 'folder': mxFolderHandler, 'cube': mxCubeHandler, 'card': mxCardHandler, 'note': mxNoteHandler, 'step': mxStepHandler, 'tape': mxTapeHandler}; // Swimlane handler Graph.prototype.createHandler = function(state) { if (state != null) { var ctor = handlers[state.style['shape']]; if (ctor != null) { return new ctor(state); } } return mxGraph.prototype.createHandler.apply(this, arguments); }; function mxSwimlaneHandler(state) { mxVertexHandler.call(this, state); }; // Graph inherits from mxGraph mxUtils.extend(mxSwimlaneHandler, mxVertexHandler); mxSwimlaneHandler.prototype.useGridForSpecialHandle = false; mxSwimlaneHandler.prototype.init = function() { this.horizontal = mxUtils.getValue(this.state.style, mxConstants.STYLE_HORIZONTAL, true); var graph = this.state.view.graph; var size = 10; var bounds = new mxRectangle(0, 0, size, size); //this.specialHandle = this.createSizerShape(bounds, -2, 'orange'); this.specialHandle = new mxRhombus(bounds, mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR); this.specialHandle.crisp = this.crisp; this.specialHandle.dialect = (graph.dialect != mxConstants.DIALECT_SVG) ? mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG; this.specialHandle.init(graph.getView().getOverlayPane()); this.specialHandle.node.style.cursor = this.getSpecialHandleCursor(); mxEvent.redirectMouseEvents(this.specialHandle.node, graph, this.state); mxVertexHandler.prototype.init.apply(this, arguments); }; mxSwimlaneHandler.prototype.getSpecialHandleCursor = function() { return (this.horizontal) ? 'n-resize' : 'w-resize'; }; mxSwimlaneHandler.prototype.redraw = function() { mxVertexHandler.prototype.redraw.apply(this, arguments); var size = this.specialHandle.bounds.width; this.specialHandle.bounds = this.getSpecialHandleBounds(size); this.specialHandle.redraw(); }; mxSwimlaneHandler.prototype.getSpecialHandleBounds = function(size) { var scale = this.graph.getView().scale; var start = this.state.view.graph.getStartSize(this.state.cell); if (this.horizontal) { return new mxRectangle(this.state.x + (this.state.width - size) / 2, this.state.y + start.height * scale - size / 2, size, size); } else { return new mxRectangle(this.state.x + start.width * scale - size / 2, this.state.y + (this.state.height - size) / 2, size, size); } }; mxSwimlaneHandler.prototype.destroy = function() { mxVertexHandler.prototype.destroy.apply(this, arguments); if (this.specialHandle != null) { this.specialHandle.destroy(); this.specialHandle = null; } }; mxSwimlaneHandler.prototype.getHandleForEvent = function(me) { if (me.isSource(this.specialHandle)) { return -2; } return mxVertexHandler.prototype.getHandleForEvent.apply(this, arguments); }; mxSwimlaneHandler.prototype.constrainPoint = function(point) { point.x = Math.max(this.state.x, Math.min(this.state.x + this.state.width, point.x)); point.y = Math.max(this.state.y, Math.min(this.state.y + this.state.height, point.y)); }; mxSwimlaneHandler.prototype.mouseMove = function(sender, me) { if (!me.isConsumed() && this.index == -2) { var point = new mxPoint(me.getGraphX(), me.getGraphY()); this.constrainPoint(point); var gridEnabled = this.graph.isGridEnabledEvent(me.getEvent()); var scale = this.graph.getView().scale; if (gridEnabled && this.useGridForSpecialHandle) { point.x = this.graph.snap(point.x / scale) * scale; point.y = this.graph.snap(point.y / scale) * scale; } this.updateStyle(point); this.moveSizerTo(this.specialHandle, point.x, point.y); this.state.view.graph.cellRenderer.redraw(this.state, true); me.consume(); } else { mxVertexHandler.prototype.mouseMove.apply(this, arguments); } }; mxSwimlaneHandler.prototype.updateStyle = function(point) { var startSize = 0; if (this.horizontal) { point.x = this.state.x + this.state.width / 2; startSize = point.y - this.state.y; } else { point.y = this.state.y + this.state.height / 2; startSize = point.x - this.state.x; } var scale = this.graph.getView().scale; this.state.style['startSize'] = Math.round(Math.max(1, startSize) / scale); }; mxSwimlaneHandler.prototype.mouseUp = function(sender, me) { if (!me.isConsumed() && this.index == -2) { this.applyStyle(); this.reset(); me.consume(); } else { mxVertexHandler.prototype.mouseUp.apply(this, arguments); } }; mxSwimlaneHandler.prototype.applyStyle = function() { this.state.view.graph.setCellStyles('startSize', this.state.style['startSize'], [this.state.cell]); }; // Folder Handler function mxFolderHandler(state) { mxSwimlaneHandler.call(this, state); }; mxUtils.extend(mxFolderHandler, mxSwimlaneHandler); mxFolderHandler.prototype.getSpecialHandleCursor = function() { return 'default'; }; mxFolderHandler.prototype.getSpecialHandleBounds = function(size) { var direction = mxUtils.getValue(this.state.style, 'direction', 'east'); var bounds = new mxRectangle(this.state.x, this.state.y, this.state.width, this.state.height); if (direction == 'south' || direction == 'north') { bounds.x += (bounds.width - bounds.height) / 2; bounds.y += (bounds.height - bounds.width) / 2; var tmp = bounds.width; bounds.width = bounds.height; bounds.height = tmp; } var pt = this.getSpecialHandlePoint(bounds); var cos = 1; var sin = 0; if (direction == 'south') { cos = 0; sin = 1; } else if (direction == 'west') { cos = -1; } else if (direction == 'north') { cos = 0; sin = -1; } pt = mxUtils.getRotatedPoint(pt, cos, sin, new mxPoint(this.state.getCenterX(), this.state.getCenterY())); return new mxRectangle(pt.x - size / 2, pt.y - size / 2, size, size); }; mxFolderHandler.prototype.getSpecialHandlePoint = function(bounds) { var scale = this.graph.getView().scale; var tw = Math.min(bounds.width, mxUtils.getValue(this.state.style, 'tabWidth', 60) * scale); var th = Math.min(bounds.height, mxUtils.getValue(this.state.style, 'tabHeight', 20) * scale); var tp = mxUtils.getValue(this.state.style, 'tabPosition', 'right'); var x = (tp == 'left') ? bounds.x + tw : bounds.x + bounds.width - tw; return new mxPoint(x, bounds.y + th); }; mxFolderHandler.prototype.updateStyle = function(point) { var pt = new mxPoint(point.x, point.y); var direction = mxUtils.getValue(this.state.style, 'direction', 'east'); var bounds = new mxRectangle(this.state.x, this.state.y, this.state.width, this.state.height); if (direction == 'south' || direction == 'north') { bounds.x += (bounds.width - bounds.height) / 2; bounds.y += (bounds.height - bounds.width) / 2; var tmp = bounds.width; bounds.width = bounds.height; bounds.height = tmp; } var cos = 1; var sin = 0; if (direction == 'south') { cos = 0; sin = -1; } else if (direction == 'west') { cos = -1; } else if (direction == 'north') { cos = 0; sin = 1; } // TODO: Rotate bounds //var bounds = new mxRectangle(this.state.x, this.state.y, this.state.width, this.state.height); pt = mxUtils.getRotatedPoint(pt, cos, sin, new mxPoint(this.state.getCenterX(), this.state.getCenterY())); var result = this.updateStyleUnrotated(pt, bounds); // Modifies point to use rotated coordinates of return value if (result != null) { if (direction == 'south' || direction == 'north') { cos *= -1; sin *= -1; } result = mxUtils.getRotatedPoint(result, cos, sin, new mxPoint(this.state.getCenterX(), this.state.getCenterY())); point.x = result.x; point.y = result.y; } }; mxFolderHandler.prototype.updateStyleUnrotated = function(pt, bounds) { var tp = mxUtils.getValue(this.state.style, 'tabPosition', 'right'); var tw = (tp == 'left') ? pt.x - bounds.x : bounds.x + bounds.width - pt.x; var th = pt.y - bounds.y; var scale = this.graph.getView().scale; this.state.style['tabWidth'] = Math.round(Math.max(1, tw) / scale); this.state.style['tabHeight'] = Math.round(Math.max(1, th) / scale); }; mxFolderHandler.prototype.applyStyle = function() { var model = this.graph.getModel(); model.beginUpdate(); try { this.state.view.graph.setCellStyles('tabWidth', this.state.style['tabWidth'], [this.state.cell]); this.state.view.graph.setCellStyles('tabHeight', this.state.style['tabHeight'], [this.state.cell]); } finally { model.endUpdate(); } }; // Cube Handler function mxCubeHandler(state) { mxFolderHandler.call(this, state); }; mxUtils.extend(mxCubeHandler, mxFolderHandler); mxCubeHandler.prototype.defaultValue = 20; mxCubeHandler.prototype.scaleFactor = 1; mxCubeHandler.prototype.getSpecialHandlePoint = function(bounds) { var scale = this.graph.getView().scale; var sz = Math.min(bounds.width, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', this.defaultValue) * scale / this.scaleFactor)); return new mxPoint(bounds.x + sz, bounds.y + sz); }; mxCubeHandler.prototype.updateStyleUnrotated = function(pt, bounds) { var size = Math.min(Math.min(bounds.width / this.scaleFactor, pt.x - bounds.x), Math.min(bounds.height / this.scaleFactor, pt.y - bounds.y)); var scale = this.graph.getView().scale; this.state.style['size'] = Math.round(Math.max(1, size) / scale) * this.scaleFactor; // Stays on the diagonal return new mxPoint(bounds.x + size, bounds.y + size); }; mxCubeHandler.prototype.applyStyle = function() { this.state.view.graph.setCellStyles('size', this.state.style['size'], [this.state.cell]); }; // Card Handler function mxCardHandler(state) { mxCubeHandler.call(this, state); }; mxUtils.extend(mxCardHandler, mxCubeHandler); mxCardHandler.prototype.defaultValue = 30; mxCardHandler.prototype.scaleFactor = 2; // Note Handler function mxNoteHandler(state) { mxCubeHandler.call(this, state); }; mxUtils.extend(mxNoteHandler, mxCubeHandler); mxNoteHandler.prototype.defaultValue = 30; mxNoteHandler.prototype.scaleFactor = 1; mxNoteHandler.prototype.getSpecialHandlePoint = function(bounds) { var scale = this.graph.getView().scale; var sz = Math.min(bounds.width, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', this.defaultValue) * scale / this.scaleFactor)); return new mxPoint(bounds.x + bounds.width - sz, bounds.y + sz); }; mxNoteHandler.prototype.updateStyleUnrotated = function(pt, bounds) { var size = Math.min(Math.min(bounds.width / this.scaleFactor, pt.x - bounds.x + bounds.width), Math.min(bounds.height / this.scaleFactor, pt.y - bounds.y)); var scale = this.graph.getView().scale; this.state.style['size'] = Math.round(Math.max(1, size) / scale) * this.scaleFactor; // Stays on the diagonal return new mxPoint(bounds.x + bounds.width - size, bounds.y + size); }; // Step Handler function mxStepHandler(state) { mxCubeHandler.call(this, state); }; mxUtils.extend(mxStepHandler, mxCubeHandler); mxStepHandler.prototype.defaultValue = 0.2; mxStepHandler.prototype.scaleFactor = 1; mxStepHandler.prototype.getSpecialHandlePoint = function(bounds) { var sz = mxUtils.getValue(this.state.style, 'size', this.defaultValue); return new mxPoint(bounds.x + bounds.width * sz, bounds.y + bounds.height / 2); }; mxStepHandler.prototype.updateStyleUnrotated = function(pt, bounds) { var size = Math.min(1, (pt.x - bounds.x) / bounds.width); this.state.style['size'] = size; return new mxPoint(bounds.x + size * bounds.width, bounds.y + bounds.height / 2); }; // Step Handler function mxTapeHandler(state) { mxCubeHandler.call(this, state); }; mxUtils.extend(mxTapeHandler, mxCubeHandler); mxTapeHandler.prototype.defaultValue = 0.4; mxTapeHandler.prototype.scaleFactor = 1; mxTapeHandler.prototype.getSpecialHandlePoint = function(bounds) { var sz = mxUtils.getValue(this.state.style, 'size', this.defaultValue); return new mxPoint(bounds.x + bounds.width / 2, bounds.y + sz * bounds.height / 2); }; mxTapeHandler.prototype.updateStyleUnrotated = function(pt, bounds) { var size = Math.min(1, ((pt.y - bounds.y) / bounds.height) * 2); this.state.style['size'] = size; return new mxPoint(bounds.x + bounds.width / 2, bounds.y + size * bounds.height / 2); }; mxGraph.prototype.getAllConnectionConstraints = function(terminal, source) { if (terminal != null && terminal.shape != null && terminal.shape instanceof mxStencilShape) { if (terminal.shape.stencil != null) { return terminal.shape.stencil.constraints; } } else if (terminal.shape.constraints != null) { return terminal.shape.constraints; } return null; }; mxRectangleShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.75, 0), true), new mxConnectionConstraint(new mxPoint(0, 0.25), true), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(0, 0.75), true), new mxConnectionConstraint(new mxPoint(1, 0.25), true), new mxConnectionConstraint(new mxPoint(1, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.75), true), new mxConnectionConstraint(new mxPoint(0.25, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; mxLabel.prototype.constraints = mxRectangleShape.prototype.constraints; mxImageShape.prototype.constraints = mxRectangleShape.prototype.constraints; mxSwimlane.prototype.constraints = mxRectangleShape.prototype.constraints; PlusShape.prototype.constraints = mxRectangleShape.prototype.constraints; NoteShape.prototype.constraints = mxRectangleShape.prototype.constraints; CardShape.prototype.constraints = mxRectangleShape.prototype.constraints; CubeShape.prototype.constraints = mxRectangleShape.prototype.constraints; FolderShape.prototype.constraints = mxRectangleShape.prototype.constraints; mxCylinder.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.15, 0.05), false), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.85, 0.05), false), new mxConnectionConstraint(new mxPoint(0, 0.3), true), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(0, 0.7), true), new mxConnectionConstraint(new mxPoint(1, 0.3), true), new mxConnectionConstraint(new mxPoint(1, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.7), true), new mxConnectionConstraint(new mxPoint(0.15, 0.95), false), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.85, 0.95), false)]; UmlActorShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.75, 0), true), new mxConnectionConstraint(new mxPoint(0, 0.25), true), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(0, 0.75), true), new mxConnectionConstraint(new mxPoint(1, 0.25), true), new mxConnectionConstraint(new mxPoint(1, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.75), true), new mxConnectionConstraint(new mxPoint(0.25, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; ComponentShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.75, 0), true), new mxConnectionConstraint(new mxPoint(0, 0.3), true), new mxConnectionConstraint(new mxPoint(0, 0.7), true), new mxConnectionConstraint(new mxPoint(1, 0.25), true), new mxConnectionConstraint(new mxPoint(1, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.75), true), new mxConnectionConstraint(new mxPoint(0.25, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; mxActor.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.25, 0.2), false), new mxConnectionConstraint(new mxPoint(0.1, 0.5), false), new mxConnectionConstraint(new mxPoint(0, 0.75), true), new mxConnectionConstraint(new mxPoint(0.75, 0.25), false), new mxConnectionConstraint(new mxPoint(0.9, 0.5), false), new mxConnectionConstraint(new mxPoint(1, 0.75), true), new mxConnectionConstraint(new mxPoint(0.25, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; TapeShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.35), false), new mxConnectionConstraint(new mxPoint(0, 0.5), false), new mxConnectionConstraint(new mxPoint(0, 0.65), false), new mxConnectionConstraint(new mxPoint(1, 0.35), false), new mxConnectionConstraint(new mxPoint(1, 0.5), false), new mxConnectionConstraint(new mxPoint(1, 0.65), false), new mxConnectionConstraint(new mxPoint(0.25, 1), false), new mxConnectionConstraint(new mxPoint(0.75, 0), false)]; // TODO: Relative ports StepShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.75, 0), true), new mxConnectionConstraint(new mxPoint(0.25, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.75, 1), true), new mxConnectionConstraint(new mxPoint(0.1, 0.25), false), new mxConnectionConstraint(new mxPoint(0.2, 0.5), false), new mxConnectionConstraint(new mxPoint(0.1, 0.75), false), new mxConnectionConstraint(new mxPoint(0.9, 0.25), false), new mxConnectionConstraint(new mxPoint(1, 0.5), false), new mxConnectionConstraint(new mxPoint(0.9, 0.75), false)]; mxLine.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), new mxConnectionConstraint(new mxPoint(0.75, 0.5), false), new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; LollipopShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.5, 0), false), new mxConnectionConstraint(new mxPoint(0.5, 1), false)]; mxEllipse.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), true), new mxConnectionConstraint(new mxPoint(1, 0), true), new mxConnectionConstraint(new mxPoint(0, 1), true), new mxConnectionConstraint(new mxPoint(1, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.5))]; mxDoubleEllipse.prototype.constraints = mxEllipse.prototype.constraints; mxRhombus.prototype.constraints = mxEllipse.prototype.constraints; mxTriangle.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.25), true), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(0, 0.75), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(1, 0.5), true)]; mxHexagon.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.375, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.625, 0), true), new mxConnectionConstraint(new mxPoint(0.125, 0.25), false), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(0.125, 0.75), false), new mxConnectionConstraint(new mxPoint(0.875, 0.25), false), new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.5), true), new mxConnectionConstraint(new mxPoint(0.875, 0.75), false), new mxConnectionConstraint(new mxPoint(0.375, 1), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), new mxConnectionConstraint(new mxPoint(0.625, 1), true)]; mxCloud.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0.25), false), new mxConnectionConstraint(new mxPoint(0.4, 0.1), false), new mxConnectionConstraint(new mxPoint(0.16, 0.55), false), new mxConnectionConstraint(new mxPoint(0.07, 0.4), false), new mxConnectionConstraint(new mxPoint(0.31, 0.8), false), new mxConnectionConstraint(new mxPoint(0.13, 0.77), false), new mxConnectionConstraint(new mxPoint(0.8, 0.8), false), new mxConnectionConstraint(new mxPoint(0.55, 0.95), false), new mxConnectionConstraint(new mxPoint(0.875, 0.5), false), new mxConnectionConstraint(new mxPoint(0.96, 0.7), false), new mxConnectionConstraint(new mxPoint(0.625, 0.2), false), new mxConnectionConstraint(new mxPoint(0.88, 0.25), false)]; })();