/** * $Id: mxShape.js,v 1.173 2012-07-31 11:46:53 gaudenz Exp $ * Copyright (c) 2006-2010, JGraph Ltd */ /** * Class: mxShape * * Base class for all shapes. A shape in mxGraph is a * separate implementation for SVG, VML and HTML. Which * implementation to use is controlled by the * property which is assigned from within the * when the shape is created. The dialect must be assigned * for a shape, and it does normally depend on the browser and * the confiuration of the graph (see rendering hint). * * For each supported shape in SVG and VML, a corresponding * shape exists in mxGraph, namely for text, image, rectangle, * rhombus, ellipse and polyline. The other shapes are a * combination of these shapes (eg. label and swimlane) * or they consist of one or more (filled) path objects * (eg. actor and cylinder). The HTML implementation is * optional but may be required for a HTML-only view of * the graph. * * Custom Shapes: * * To extend from this class, the basic code looks as follows. * In the special case where the custom shape consists only of * one filled region or one filled region and an additional stroke * the and should be subclassed, * respectively. These implement in order to create * the path expression for VML and SVG via a unified API (see * ). has an additional boolean * argument to draw the foreground and background separately. * * (code) * function CustomShape() { } * * CustomShape.prototype = new mxShape(); * CustomShape.prototype.constructor = CustomShape; * (end) * * To register a custom shape in an existing graph instance, * one must register the shape under a new name in the graph's * cell renderer as follows: * * (code) * graph.cellRenderer.registerShape('customShape', CustomShape); * (end) * * The second argument is the name of the constructor. * * In order to use the shape you can refer to the given name above * in a stylesheet. For example, to change the shape for the default * vertex style, the following code is used: * * (code) * var style = graph.getStylesheet().getDefaultVertexStyle(); * style[mxConstants.STYLE_SHAPE] = 'customShape'; * (end) * * Constructor: mxShape * * Constructs a new shape. */ function mxShape() { }; /** * Variable: SVG_STROKE_TOLERANCE * * Event-tolerance for SVG strokes (in px). Default is 8. */ mxShape.prototype.SVG_STROKE_TOLERANCE = 8; /** * Variable: scale * * Holds the scale in which the shape is being painted. */ mxShape.prototype.scale = 1; /** * Variable: dialect * * Holds the dialect in which the shape is to be painted. * This can be one of the DIALECT constants in . */ mxShape.prototype.dialect = null; /** * Variable: crisp * * Special attribute for SVG rendering to set the shape-rendering attribute to * crispEdges in the output. This is ignored in IE. Default is false. To * disable antialias in IE, the explorer.css file can be changed as follows: * * [code] * v\:* { * behavior: url(#default#VML); * antialias: false; * } * [/code] */ mxShape.prototype.crisp = false; /** * Variable: roundedCrispSvg * * Specifies if crisp rendering should be enabled for rounded shapes. * Default is true. */ mxShape.prototype.roundedCrispSvg = true; /** * Variable: mixedModeHtml * * Specifies if should be used in mixed Html mode. * Default is true. */ mxShape.prototype.mixedModeHtml = true; /** * Variable: preferModeHtml * * Specifies if should be used in prefer Html mode. * Default is true. */ mxShape.prototype.preferModeHtml = true; /** * Variable: bounds * * Holds the that specifies the bounds of this shape. */ mxShape.prototype.bounds = null; /** * Variable: points * * Holds the array of that specify the points of this shape. */ mxShape.prototype.points = null; /** * Variable: node * * Holds the outermost DOM node that represents this shape. */ mxShape.prototype.node = null; /** * Variable: label * * Reference to the DOM node that should contain the label. This is null * if the label should be placed inside or . */ mxShape.prototype.label = null; /** * Variable: innerNode * * Holds the DOM node that graphically represents this shape. This may be * null if the outermost DOM represents this shape. */ mxShape.prototype.innerNode = null; /** * Variable: style * * Holds the style of the cell state that corresponds to this shape. This may * be null if the shape is used directly, without a cell state. */ mxShape.prototype.style = null; /** * Variable: startOffset * * Specifies the offset in pixels from the first point in and * the actual start of the shape. */ mxShape.prototype.startOffset = null; /** * Variable: endOffset * * Specifies the offset in pixels from the last point in and * the actual start of the shape. */ mxShape.prototype.endOffset = null; /** * Variable: boundingBox * * Contains the bounding box of the shape, that is, the smallest rectangle * that includes all pixels of the shape. */ mxShape.prototype.boundingBox = null; /** * Variable: vmlNodes * * Array if VML node names to fix in IE8 standards mode. */ mxShape.prototype.vmlNodes = ['node', 'strokeNode', 'fillNode', 'shadowNode']; /** * Variable: vmlScale * * Internal scaling for VML using coordsize for better precision. */ mxShape.prototype.vmlScale = 1; /** * Variable: strokewidth * * Holds the current strokewidth. Default is 1. */ mxShape.prototype.strokewidth = 1; /** * Function: setCursor * * Sets the cursor on the given shape. * * Parameters: * * cursor - The cursor to be used. */ mxShape.prototype.setCursor = function(cursor) { if (cursor == null) { cursor = ''; } this.cursor = cursor; if (this.innerNode != null) { this.innerNode.style.cursor = cursor; } if (this.node != null) { this.node.style.cursor = cursor; } if (this.pipe != null) { this.pipe.style.cursor = cursor; } }; /** * Function: getCursor * * Returns the current cursor. */ mxShape.prototype.getCursor = function() { return this.cursor; }; /** * Function: init * * Initializes the shape by creaing the DOM node using * and adding it into the given container. * * Parameters: * * container - DOM node that will contain the shape. */ mxShape.prototype.init = function(container) { if (this.node == null) { this.node = this.create(container); if (container != null) { container.appendChild(this.node); // Workaround for broken VML in IE8 standards mode. This gives an ID to // each element that is referenced from this instance. After adding the // DOM to the document, the outerHTML is overwritten to fix the VML // rendering and the references are restored. if (document.documentMode == 8 && mxUtils.isVml(this.node)) { this.reparseVml(); } } } // Gradients are inserted late when the owner SVG element is known if (this.insertGradientNode != null) { this.insertGradient(this.insertGradientNode); this.insertGradientNode = null; } }; /** * Function: reparseVml * * Forces a parsing of the outerHTML of this node and restores all references specified in . * This is a workaround for the VML rendering bug in IE8 standards mode. */ mxShape.prototype.reparseVml = function() { // Assigns temporary IDs to VML nodes so that references can be restored when // inserted into the DOM as a string for (var i = 0; i < this.vmlNodes.length; i++) { if (this[this.vmlNodes[i]] != null) { this[this.vmlNodes[i]].setAttribute('id', 'mxTemporaryReference-' + this.vmlNodes[i]); } } this.node.outerHTML = this.node.outerHTML; // Restores references to the actual DOM nodes for (var i = 0; i < this.vmlNodes.length; i++) { if (this[this.vmlNodes[i]] != null) { this[this.vmlNodes[i]] = this.node.ownerDocument.getElementById('mxTemporaryReference-' + this.vmlNodes[i]); this[this.vmlNodes[i]].removeAttribute('id'); } } }; /** * Function: insertGradient * * Inserts the given gradient node. */ mxShape.prototype.insertGradient = function(node) { // Gradients are inserted late when the owner SVG element is known if (node != null) { // Checks if the given gradient already exists inside the SVG element // that also contains the node that represents this shape. If the gradient // with the same ID exists in another SVG element, then this will add // a copy of the gradient with a different ID to the SVG element and update // the reference accordingly. This is required in Firefox because if the // referenced fill element is removed from the DOM the shape appears black. var count = 0; var id = node.getAttribute('id'); var gradient = document.getElementById(id); while (gradient != null && gradient.ownerSVGElement != this.node.ownerSVGElement) { count++; id = node.getAttribute('id') + '-' + count; gradient = document.getElementById(id); } // According to specification, gradients should be put in a defs // section in the first child of the owner SVG element. However, // it turns out that gradients only work when added as follows. if (gradient == null) { node.setAttribute('id', id); this.node.ownerSVGElement.appendChild(node); gradient = node; } if (gradient != null) { var ref = 'url(#' + id + ')'; var tmp = (this.innerNode != null) ? this.innerNode : this.node; if (tmp != null && tmp.getAttribute('fill') != ref) { tmp.setAttribute('fill', ref); } } } }; /** * Function: isMixedModeHtml * * Used to determine if a shape can be rendered using in mixed * mode Html without compromising the display accuracy. The default * implementation will check if the shape is not rounded or rotated and has * no gradient, and will use a DIV if that is the case. It will also check * if is true, which is the default settings. * Subclassers can either override or this function if the * result depends on dynamic values. The graph's dialect is available via * . */ mxShape.prototype.isMixedModeHtml = function() { return this.mixedModeHtml && !this.isRounded && !this.isShadow && this.gradient == null && mxUtils.getValue(this.style, mxConstants.STYLE_GLASS, 0) == 0 && mxUtils.getValue(this.style, mxConstants.STYLE_ROTATION, 0) == 0; }; /** * Function: create * * Creates and returns the DOM node(s) for the shape in * the given container. This implementation invokes * , or depending * on the and style settings. * * Parameters: * * container - DOM node that will contain the shape. */ mxShape.prototype.create = function(container) { var node = null; if (this.dialect == mxConstants.DIALECT_SVG) { node = this.createSvg(); } else if (this.dialect == mxConstants.DIALECT_STRICTHTML || (this.preferModeHtml && this.dialect == mxConstants.DIALECT_PREFERHTML) || (this.isMixedModeHtml() && this.dialect == mxConstants.DIALECT_MIXEDHTML)) { node = this.createHtml(); } else { node = this.createVml(); } return node; }; /** * Function: createHtml * * Creates and returns the HTML DOM node(s) to represent * this shape. This implementation falls back to * so that the HTML creation is optional. */ mxShape.prototype.createHtml = function() { var node = document.createElement('DIV'); this.configureHtmlShape(node); return node; }; /** * Function: destroy * * Destroys the shape by removing it from the DOM and releasing the DOM * node associated with the shape using . */ mxShape.prototype.destroy = function() { if (this.node != null) { mxEvent.release(this.node); if (this.node.parentNode != null) { this.node.parentNode.removeChild(this.node); } if (this.node.glassOverlay) { this.node.glassOverlay.parentNode.removeChild(this.node.glassOverlay); this.node.glassOverlay = null; } this.node = null; } }; /** * Function: apply * * Applies the style of the given to the shape. This * implementation assigns the following styles to local fields: * * - => fill * - => gradient * - => gradientDirection * - => opacity * - => stroke * - => strokewidth * - => isShadow * - => isDashed * - => spacing * - => startSize * - => endSize * - => isRounded * - => startArrow * - => endArrow * - => rotation * - => direction * * This keeps a reference to the