summaryrefslogtreecommitdiff
path: root/src/js/shape/mxStencil.js
diff options
context:
space:
mode:
authoradhitya2016-04-12 07:02:39 +0000
committeradhitya2016-04-12 07:02:39 +0000
commitdd83478e3fcaac98de690aa59e6288ad41a1c351 (patch)
tree38653bdf0ae95053f66777c4ac3fe5be5d8fbd33 /src/js/shape/mxStencil.js
parent92f3207b50a1caca07df5c5b238212af3358905b (diff)
downloadxcos-on-web-dd83478e3fcaac98de690aa59e6288ad41a1c351.tar.gz
xcos-on-web-dd83478e3fcaac98de690aa59e6288ad41a1c351.tar.bz2
xcos-on-web-dd83478e3fcaac98de690aa59e6288ad41a1c351.zip
Keyboard shortcuts work properly
Diffstat (limited to 'src/js/shape/mxStencil.js')
-rw-r--r--src/js/shape/mxStencil.js1585
1 files changed, 0 insertions, 1585 deletions
diff --git a/src/js/shape/mxStencil.js b/src/js/shape/mxStencil.js
deleted file mode 100644
index d0e1a63..0000000
--- a/src/js/shape/mxStencil.js
+++ /dev/null
@@ -1,1585 +0,0 @@
-/**
- * $Id: mxStencil.js,v 1.91 2012-07-16 10:22:44 gaudenz Exp $
- * Copyright (c) 2006-2010, JGraph Ltd
- */
-/**
- * Class: mxStencil
- *
- * Implements a generic shape which is based on a XML node as a description.
- * The node contains a background and a foreground node, which contain the
- * definition to render the respective part of the shape. Note that the
- * fill, stroke or fillstroke of the background is be the first statement
- * of the foreground. This is because the content of the background node
- * maybe used to not only render the shape itself, but also its shadow and
- * other elements which do not require a fill, stroke or fillstroke.
- *
- * The shape uses a coordinate system with a width of 100 and a height of
- * 100 by default. This can be changed by setting the w and h attribute of
- * the shape element. The aspect attribute can be set to "variable" (default)
- * or "fixed". If fixed is used, then the aspect which is defined via the w
- * and h attribute is kept constant while the shape is scaled.
- *
- * The possible contents of the background and foreground elements are rect,
- * ellipse, roundrect, text, image, include-shape or paths. A path element
- * contains move, line, curve, quad, arc and close elements. The rect, ellipse
- * and roundrect elements may be thought of as special path elements. All these
- * path elements must be followed by either fill, stroke or fillstroke (note
- * that text, image and include-shape or not path elements).
- *
- * The background element can be empty or contain at most one path element. It
- * should not contain a text, image or include-shape element. If the background
- * element is empty, then no shadow or glass effect will be rendered. If the
- * background element is non-empty, then the corresponding fill, stroke or
- * fillstroke should be the first element in the subsequent foreground element.
- *
- * The format of the XML is "a simplified HTML 5 Canvas". Each command changes
- * the "current" state, so eg. a linecap, linejoin will be used for all
- * subsequent line drawing, unless a save/restore appears, which saves/restores
- * a state in a stack.
- *
- * The connections section contains the fixed connection points for a stencil.
- * The perimeter attribute of the constraint element should have a value of 0
- * or 1 (default), where 1 (true) specifies that the given point should be
- * projected into the perimeter of the given shape.
- *
- * The x- and y-coordinates are typically between 0 and 1 and define the
- * location of the connection point relative to the width and height of the
- * shape.
- *
- * The dashpattern directive sets the current dashpattern. The format for the
- * pattern attribute is a space-separated sequence of numbers, eg. 5 5 5 5,
- * that specifies the lengths of alternating dashes and spaces in dashed lines.
- * The dashpattern should be used together with the dashed directive to
- * enabled/disable the dashpattern. The default dashpattern is 3 3.
- *
- * The strokewidth attribute defines a fixed strokewidth for the shape. It
- * can contain a numeric value or the keyword "inherit", which means that the
- * strokeWidth from the cell's style will be used and muliplied with the shape's
- * scale. If numeric values are used, those are multiplied with the minimum
- * scale used to render the stencil inside the shape's bounds.
- *
- * Constructor: mxStencilShape
- *
- * Constructs a new generic shape by setting <desc> to the given XML node and
- * invoking <parseDescription> and <parseConstraints>.
- *
- * Parameters:
- *
- * desc - XML node that contains the stencil description.
- */
-function mxStencil(desc)
-{
- this.desc = desc;
- this.parseDescription();
- this.parseConstraints();
-};
-
-/**
- * Variable: desc
- *
- * Holds the XML node with the stencil description.
- */
-mxStencil.prototype.desc = null;
-
-/**
- * Variable: constraints
- *
- * Holds an array of <mxConnectionConstraints> as defined in the shape.
- */
-mxStencil.prototype.constraints = null;
-
-/**
- * Variable: aspect
- *
- * Holds the aspect of the shape. Default is 'auto'.
- */
-mxStencil.prototype.aspect = null;
-
-/**
- * Variable: w0
- *
- * Holds the width of the shape. Default is 100.
- */
-mxStencil.prototype.w0 = null;
-
-/**
- * Variable: h0
- *
- * Holds the height of the shape. Default is 100.
- */
-mxStencil.prototype.h0 = null;
-
-/**
- * Variable: bgNodes
- *
- * Holds the XML node with the stencil description.
- */
-mxStencil.prototype.bgNode = null;
-
-/**
- * Variable: fgNodes
- *
- * Holds the XML node with the stencil description.
- */
-mxStencil.prototype.fgNode = null;
-
-/**
- * Variable: strokewidth
- *
- * Holds the strokewidth direction from the description.
- */
-mxStencil.prototype.strokewidth = null;
-
-/**
- * Function: parseDescription
- *
- * Reads <w0>, <h0>, <aspect>, <bgNodes> and <fgNodes> from <desc>.
- */
-mxStencil.prototype.parseDescription = function()
-{
- // LATER: Preprocess nodes for faster painting
- this.fgNode = this.desc.getElementsByTagName('foreground')[0];
- this.bgNode = this.desc.getElementsByTagName('background')[0];
- this.w0 = Number(this.desc.getAttribute('w') || 100);
- this.h0 = Number(this.desc.getAttribute('h') || 100);
-
- // Possible values for aspect are: variable and fixed where
- // variable means fill the available space and fixed means
- // use w0 and h0 to compute the aspect.
- var aspect = this.desc.getAttribute('aspect');
- this.aspect = (aspect != null) ? aspect : 'variable';
-
- // Possible values for strokewidth are all numbers and "inherit"
- // where the inherit means take the value from the style (ie. the
- // user-defined stroke-width). Note that the strokewidth is scaled
- // by the minimum scaling that is used to draw the shape (sx, sy).
- var sw = this.desc.getAttribute('strokewidth');
- this.strokewidth = (sw != null) ? sw : '1';
-};
-
-/**
- * Function: parseConstraints
- *
- * Reads the constraints from <desc> into <constraints> using
- * <parseConstraint>.
- */
-mxStencil.prototype.parseConstraints = function()
-{
- var conns = this.desc.getElementsByTagName('connections')[0];
-
- if (conns != null)
- {
- var tmp = mxUtils.getChildNodes(conns);
-
- if (tmp != null && tmp.length > 0)
- {
- this.constraints = [];
-
- for (var i = 0; i < tmp.length; i++)
- {
- this.constraints.push(this.parseConstraint(tmp[i]));
- }
- }
- }
-};
-
-/**
- * Function: parseConstraint
- *
- * Parses the given XML node and returns its <mxConnectionConstraint>.
- */
-mxStencil.prototype.parseConstraint = function(node)
-{
- var x = Number(node.getAttribute('x'));
- var y = Number(node.getAttribute('y'));
- var perimeter = node.getAttribute('perimeter') == '1';
-
- return new mxConnectionConstraint(new mxPoint(x, y), perimeter);
-};
-
-/**
- * Function: evaluateAttribute
- *
- * Gets the attribute for the given name from the given node. If the attribute
- * does not exist then the text content of the node is evaluated and if it is
- * a function it is invoked with <state> as the only argument and the return
- * value is used as the attribute value to be returned.
- */
-mxStencil.prototype.evaluateAttribute = function(node, attribute, state)
-{
- var result = node.getAttribute(attribute);
-
- if (result == null)
- {
- var text = mxUtils.getTextContent(node);
-
- if (text != null)
- {
- var funct = mxUtils.eval(text);
-
- if (typeof(funct) == 'function')
- {
- result = funct(state);
- }
- }
- }
-
- return result;
-};
-
-/**
- * Function: renderDom
- *
- * Updates the SVG or VML shape.
- */
-mxStencil.prototype.renderDom = function(shape, bounds, parentNode, state)
-{
- var vml = shape.dialect != mxConstants.DIALECT_SVG;
- var vmlScale = (document.documentMode == 8) ? 1 : shape.vmlScale;
- var rotation = shape.rotation || 0;
- var inverse = false;
-
- // New styles for shape flipping the stencil
- var flipH = shape.style[mxConstants.STYLE_STENCIL_FLIPH];
- var flipV = shape.style[mxConstants.STYLE_STENCIL_FLIPV];
-
- if (flipH ? !flipV : flipV)
- {
- rotation *= -1;
- }
-
- // Default direction is east (ignored if rotation exists)
- if (shape.direction != null)
- {
- if (shape.direction == 'north')
- {
- rotation += 270;
- }
- else if (shape.direction == 'west')
- {
- rotation += 180;
- }
- else if (shape.direction == 'south')
- {
- rotation += 90;
- }
-
- inverse = (shape.direction == 'north' || shape.direction == 'south');
- }
-
- if (flipH && flipV)
- {
- rotation += 180;
- flipH = false;
- flipV = false;
- }
-
- // SVG transform should be applied on all child shapes
- var svgTransform = '';
-
- // Implements direction style and vertical/horizontal flip
- // via container transformation.
- if (vml)
- {
- if (flipH)
- {
- parentNode.style.flip = 'x';
- }
- else if (flipV)
- {
- parentNode.style.flip = 'y';
- }
-
- if (rotation != 0)
- {
- parentNode.style.rotation = rotation;
- }
- }
- else
- {
- if (flipH || flipV)
- {
- var sx = 1;
- var sy = 1;
- var dx = 0;
- var dy = 0;
-
- if (flipH)
- {
- sx = -1;
- dx = -bounds.width - 2 * bounds.x;
- }
-
- if (flipV)
- {
- sy = -1;
- dy = -bounds.height - 2 * bounds.y;
- }
-
- svgTransform = 'scale(' + sx + ' ' + sy + ') translate(' + dx + ' ' + dy + ')';
- }
-
- // Adds rotation as a separate transform
- if (rotation != 0)
- {
- var cx = bounds.getCenterX();
- var cy = bounds.getCenterY();
- svgTransform += ' rotate(' + rotation + ' ' + cx + ' ' + cy + ')';
- }
- }
-
- var background = (state == null);
-
- if (this.bgNode != null || this.fgNode != null)
- {
- var x0 = (vml && state == null) ? 0 : bounds.x;
- var y0 = (vml && state == null) ? 0 : bounds.y;
- var sx = bounds.width / this.w0;
- var sy = bounds.height / this.h0;
-
- // Stores current location inside path
- this.lastMoveX = 0;
- this.lastMoveY = 0;
-
- if (inverse)
- {
- sy = bounds.width / this.h0;
- sx = bounds.height / this.w0;
-
- var delta = (bounds.width - bounds.height) / 2;
-
- x0 += delta;
- y0 -= delta;
- }
-
- if (this.aspect == 'fixed')
- {
- sy = Math.min(sx, sy);
- sx = sy;
-
- // Centers the shape inside the available space
- if (inverse)
- {
- x0 += (bounds.height - this.w0 * sx) / 2;
- y0 += (bounds.width - this.h0 * sy) / 2;
- }
- else
- {
- x0 += (bounds.width - this.w0 * sx) / 2;
- y0 += (bounds.height - this.h0 * sy) / 2;
- }
- }
-
- // Workaround to improve VML rendering precision.
- if (vml)
- {
- sx *= vmlScale;
- sy *= vmlScale;
- x0 *= vmlScale;
- y0 *= vmlScale;
- }
-
- var minScale = Math.min(sx, sy);
-
- // Stack of states for save/restore ops
- var stack = [];
-
- var currentState = (state != null) ? state :
- {
- fillColorAssigned: false,
- fill: shape.fill,
- stroke: shape.stroke,
- strokeWidth: (this.strokewidth == 'inherit') ?
- Number(shape.strokewidth) * shape.scale :
- Number(this.strokewidth) * minScale / ((vml) ? vmlScale : 1),
- dashed: shape.isDashed,
- dashpattern: [3, 3],
- alpha: shape.opacity,
- linejoin: 'miter',
- fontColor: '#000000',
- fontSize: mxConstants.DEFAULT_FONTSIZE,
- fontFamily: mxConstants.DEFAULT_FONTFAMILY,
- fontStyle: 0
- };
-
- var currentPath = null;
- var currentPoints = null;
-
- var configurePath = function(path, state)
- {
- var sw = Math.max(1, state.strokeWidth);
-
- if (vml)
- {
- path.strokeweight = Math.round(sw) + 'px';
-
- if (state.fill != null)
- {
- // Gradient in foregrounds not supported because special gradients
- // with bounds must be created for each element in graphics-canvases
- var gradient = (!state.fillColorAssigned) ? shape.gradient : null;
- var fill = document.createElement('v:fill');
- shape.updateVmlFill(fill, state.fill, gradient, shape.gradientDirection, state.alpha);
- path.appendChild(fill);
- }
- else
- {
- path.filled = 'false';
- }
-
- if (state.stroke != null)
- {
- path.stroked = 'true';
- path.strokecolor = state.stroke;
- }
- else
- {
- path.stroked = 'false';
- }
-
- path.style.position = 'absolute';
- }
- else
- {
- path.setAttribute('stroke-width', sw);
-
- if (state.fill != null && state.fillColorAssigned)
- {
- path.setAttribute('fill', state.fill);
- }
-
- if (state.stroke != null)
- {
- path.setAttribute('stroke', state.stroke);
- }
- }
- };
-
- var addToPath = function(s)
- {
- if (currentPath != null && currentPoints != null)
- {
- currentPoints.push(s);
- }
- };
-
- var round = function(value)
- {
- return (vml) ? Math.round(value) : value;
- };
-
- // Will be moved to a hook later for example to set text values
- var renderNode = function(node)
- {
- var name = node.nodeName;
-
- var fillOp = name == 'fill';
- var strokeOp = name == 'stroke';
- var fillStrokeOp = name == 'fillstroke';
-
- if (name == 'save')
- {
- stack.push(currentState);
- currentState = mxUtils.clone(currentState);
- }
- else if (name == 'restore')
- {
- currentState = stack.pop();
- }
- else if (name == 'path')
- {
- currentPoints = [];
-
- if (vml)
- {
- currentPath = document.createElement('v:shape');
- configurePath.call(this, currentPath, currentState);
- var w = Math.round(bounds.width) * vmlScale;
- var h = Math.round(bounds.height) * vmlScale;
- currentPath.style.width = w + 'px';
- currentPath.style.height = h + 'px';
- currentPath.coordsize = w + ',' + h;
- }
- else
- {
- currentPath = document.createElementNS(mxConstants.NS_SVG, 'path');
- configurePath.call(this, currentPath, currentState);
-
- if (svgTransform.length > 0)
- {
- currentPath.setAttribute('transform', svgTransform);
- }
-
- if (node.getAttribute('crisp') == '1')
- {
- currentPath.setAttribute('shape-rendering', 'crispEdges');
- }
- }
-
- // Renders the elements inside the given path
- var childNode = node.firstChild;
-
- while (childNode != null)
- {
- if (childNode.nodeType == mxConstants.NODETYPE_ELEMENT)
- {
- renderNode.call(this, childNode);
- }
-
- childNode = childNode.nextSibling;
- }
-
- // Ends the current path
- if (vml)
- {
- addToPath('e');
- currentPath.path = currentPoints.join('');
- }
- else
- {
- currentPath.setAttribute('d', currentPoints.join(''));
- }
- }
- else if (name == 'move')
- {
- var op = (vml) ? 'm' : 'M';
- this.lastMoveX = round(x0 + Number(node.getAttribute('x')) * sx);
- this.lastMoveY = round(y0 + Number(node.getAttribute('y')) * sy);
- addToPath(op + ' ' + this.lastMoveX + ' ' + this.lastMoveY);
- }
- else if (name == 'line')
- {
- var op = (vml) ? 'l' : 'L';
- this.lastMoveX = round(x0 + Number(node.getAttribute('x')) * sx);
- this.lastMoveY = round(y0 + Number(node.getAttribute('y')) * sy);
- addToPath(op + ' ' + this.lastMoveX + ' ' + this.lastMoveY);
- }
- else if (name == 'quad')
- {
- if (vml)
- {
- var cpx0 = this.lastMoveX;
- var cpy0 = this.lastMoveY;
- var qpx1 = x0 + Number(node.getAttribute('x1')) * sx;
- var qpy1 = y0 + Number(node.getAttribute('y1')) * sy;
- var cpx3 = x0 + Number(node.getAttribute('x2')) * sx;
- var cpy3 = y0 + Number(node.getAttribute('y2')) * sy;
-
- var cpx1 = cpx0 + 2/3 * (qpx1 - cpx0);
- var cpy1 = cpy0 + 2/3 * (qpy1 - cpy0);
-
- var cpx2 = cpx3 + 2/3 * (qpx1 - cpx3);
- var cpy2 = cpy3 + 2/3 * (qpy1 - cpy3);
-
- addToPath('c ' + Math.round(cpx1) + ' ' + Math.round(cpy1) + ' ' +
- Math.round(cpx2) + ' ' + Math.round(cpy2) + ' ' +
- Math.round(cpx3) + ' ' + Math.round(cpy3));
-
- this.lastMoveX = cpx3;
- this.lastMoveY = cpy3;
- }
- else
- {
- this.lastMoveX = x0 + Number(node.getAttribute('x2')) * sx;
- this.lastMoveY = y0 + Number(node.getAttribute('y2')) * sy;
-
- addToPath('Q ' + (x0 + Number(node.getAttribute('x1')) * sx) + ' ' +
- (y0 + Number(node.getAttribute('y1')) * sy) + ' ' +
- this.lastMoveX + ' ' + this.lastMoveY);
- }
- }
- else if (name == 'curve')
- {
- var op = (vml) ? 'c' : 'C';
- this.lastMoveX = round(x0 + Number(node.getAttribute('x3')) * sx);
- this.lastMoveY = round(y0 + Number(node.getAttribute('y3')) * sy);
-
- addToPath(op + ' ' + round(x0 + Number(node.getAttribute('x1')) * sx) + ' ' +
- round(y0 + Number(node.getAttribute('y1')) * sy) + ' ' +
- round(x0 + Number(node.getAttribute('x2')) * sx) + ' ' +
- round(y0 + Number(node.getAttribute('y2')) * sy) + ' ' +
- this.lastMoveX + ' ' + this.lastMoveY);
- }
- else if (name == 'close')
- {
- addToPath((vml) ? 'x' : 'Z');
- }
- else if (name == 'rect' || name == 'roundrect')
- {
- var rounded = name == 'roundrect';
- var x = round(x0 + Number(node.getAttribute('x')) * sx);
- var y = round(y0 + Number(node.getAttribute('y')) * sy);
- var w = round(Number(node.getAttribute('w')) * sx);
- var h = round(Number(node.getAttribute('h')) * sy);
-
- var arcsize = node.getAttribute('arcsize');
-
- if (arcsize == 0)
- {
- arcsize = mxConstants.RECTANGLE_ROUNDING_FACTOR * 100;
- }
-
- if (vml)
- {
- // LATER: Use HTML for non-rounded, gradientless rectangles
- currentPath = document.createElement((rounded) ? 'v:roundrect' : 'v:rect');
- currentPath.style.left = x + 'px';
- currentPath.style.top = y + 'px';
- currentPath.style.width = w + 'px';
- currentPath.style.height = h + 'px';
-
- if (rounded)
- {
- currentPath.setAttribute('arcsize', String(arcsize) + '%');
- }
- }
- else
- {
- currentPath = document.createElementNS(mxConstants.NS_SVG, 'rect');
- currentPath.setAttribute('x', x);
- currentPath.setAttribute('y', y);
- currentPath.setAttribute('width', w);
- currentPath.setAttribute('height', h);
-
- if (rounded)
- {
- var factor = Number(arcsize) / 100;
- var r = Math.min(w * factor, h * factor);
- currentPath.setAttribute('rx', r);
- currentPath.setAttribute('ry', r);
- }
-
- if (svgTransform.length > 0)
- {
- currentPath.setAttribute('transform', svgTransform);
- }
-
- if (node.getAttribute('crisp') == '1')
- {
- currentPath.setAttribute('shape-rendering', 'crispEdges');
- }
- }
-
- configurePath.call(this, currentPath, currentState);
- }
- else if (name == 'ellipse')
- {
- var x = round(x0 + Number(node.getAttribute('x')) * sx);
- var y = round(y0 + Number(node.getAttribute('y')) * sy);
- var w = round(Number(node.getAttribute('w')) * sx);
- var h = round(Number(node.getAttribute('h')) * sy);
-
- if (vml)
- {
- currentPath = document.createElement('v:arc');
- currentPath.startangle = '0';
- currentPath.endangle = '360';
- currentPath.style.left = x + 'px';
- currentPath.style.top = y + 'px';
- currentPath.style.width = w + 'px';
- currentPath.style.height = h + 'px';
- }
- else
- {
- currentPath = document.createElementNS(mxConstants.NS_SVG, 'ellipse');
- currentPath.setAttribute('cx', x + w / 2);
- currentPath.setAttribute('cy', y + h / 2);
- currentPath.setAttribute('rx', w / 2);
- currentPath.setAttribute('ry', h / 2);
-
- if (svgTransform.length > 0)
- {
- currentPath.setAttribute('transform', svgTransform);
- }
- }
-
- configurePath.call(this, currentPath, currentState);
- }
- else if (name == 'arc')
- {
- var r1 = Number(node.getAttribute('rx')) * sx;
- var r2 = Number(node.getAttribute('ry')) * sy;
- var angle = Number(node.getAttribute('x-axis-rotation'));
- var largeArcFlag = Number(node.getAttribute('large-arc-flag'));
- var sweepFlag = Number(node.getAttribute('sweep-flag'));
- var x = x0 + Number(node.getAttribute('x')) * sx;
- var y = y0 + Number(node.getAttribute('y')) * sy;
-
- if (vml)
- {
- var curves = mxUtils.arcToCurves(this.lastMoveX, this.lastMoveY, r1, r2, angle, largeArcFlag, sweepFlag, x, y);
-
- for (var i = 0; i < curves.length; i += 6)
- {
- addToPath('c' + ' ' + Math.round(curves[i]) + ' ' + Math.round(curves[i + 1]) + ' ' +
- Math.round(curves[i + 2]) + ' ' + Math.round(curves[i + 3]) + ' ' +
- Math.round(curves[i + 4]) + ' ' + Math.round(curves[i + 5]));
-
- this.lastMoveX = curves[i + 4];
- this.lastMoveY = curves[i + 5];
- }
- }
- else
- {
- addToPath('A ' + r1 + ',' + r2 + ' ' + angle + ' ' + largeArcFlag + ',' + sweepFlag + ' ' + x + ',' + y);
- this.lastMoveX = x0 + x;
- this.lastMoveY = y0 + y;
- }
- }
- else if (name == 'image')
- {
- var src = this.evaluateAttribute(node, 'src', shape.state);
-
- if (src != null)
- {
- var x = round(x0 + Number(node.getAttribute('x')) * sx);
- var y = round(y0 + Number(node.getAttribute('y')) * sy);
- var w = round(Number(node.getAttribute('w')) * sx);
- var h = round(Number(node.getAttribute('h')) * sy);
-
- // TODO: _Not_ providing an aspect in the shapes format has the advantage
- // of not needing a callback to adjust the image in VML. Since the shape
- // developer can specify the aspect via width and height this should OK.
- //var aspect = node.getAttribute('aspect') != '0';
- var aspect = false;
- var flipH = node.getAttribute('flipH') == '1';
- var flipV = node.getAttribute('flipV') == '1';
-
- if (vml)
- {
- currentPath = document.createElement('v:image');
- currentPath.style.filter = 'alpha(opacity=' + currentState.alpha + ')';
- currentPath.style.left = x + 'px';
- currentPath.style.top = y + 'px';
- currentPath.style.width = w + 'px';
- currentPath.style.height = h + 'px';
- currentPath.src = src;
-
- if (flipH && flipV)
- {
- currentPath.style.rotation = '180';
- }
- else if (flipH)
- {
- currentPath.style.flip = 'x';
- }
- else if (flipV)
- {
- currentPath.style.flip = 'y';
- }
- }
- else
- {
- currentPath = document.createElementNS(mxConstants.NS_SVG, 'image');
- currentPath.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', src);
- currentPath.setAttribute('opacity', currentState.alpha / 100);
- currentPath.setAttribute('x', x);
- currentPath.setAttribute('y', y);
- currentPath.setAttribute('width', w);
- currentPath.setAttribute('height', h);
-
- if (!aspect)
- {
- currentPath.setAttribute('preserveAspectRatio', 'none');
- }
-
- if (flipH || flipV)
- {
- var scx = 1;
- var scy = 1;
- var dx = 0;
- var dy = 0;
-
- if (flipH)
- {
- scx = -1;
- dx = -w - 2 * x;
- }
-
- if (flipV)
- {
- scy = -1;
- dy = -h - 2 * y;
- }
-
- currentPath.setAttribute('transform', svgTransform + 'scale(' + scx + ' ' + scy + ')' +
- ' translate('+dx+' '+dy+') ');
- }
- else
- {
- currentPath.setAttribute('transform', svgTransform);
- }
- }
-
- parentNode.appendChild(currentPath);
- }
- }
- else if (name == 'include-shape')
- {
- var stencil = mxStencilRegistry.getStencil(node.getAttribute('name'));
-
- if (stencil != null)
- {
- var x = x0 + Number(node.getAttribute('x')) * sx;
- var y = y0 + Number(node.getAttribute('y')) * sy;
- var w = Number(node.getAttribute('w')) * sx;
- var h = Number(node.getAttribute('h')) * sy;
-
- stencil.renderDom(shape, new mxRectangle(x, y, w, h), parentNode, currentState);
- }
- }
- // Additional labels are currently disabled. Needs fixing of VML
- // text positon, SVG text rotation and ignored baseline in FF
- else if (name == 'text')
- {
- var str = this.evaluateAttribute(node, 'str', shape.state);
-
- if (str != null)
- {
- var x = round(x0 + Number(node.getAttribute('x')) * sx);
- var y = round(y0 + Number(node.getAttribute('y')) * sy);
- var align = node.getAttribute('align') || 'left';
- var valign = node.getAttribute('valign') || 'top';
-
- if (vml)
- {
- // Renders a single line of text with full rotation support
- currentPath = document.createElement('v:shape');
- currentPath.style.position = 'absolute';
- currentPath.style.width = '1px';
- currentPath.style.height = '1px';
- currentPath.style.left = x + 'px';
- currentPath.style.top = y + 'px';
-
- var fill = document.createElement('v:fill');
- fill.color = currentState.fontColor;
- fill.on = 'true';
- currentPath.appendChild(fill);
-
- var stroke = document.createElement('v:stroke');
- stroke.on = 'false';
- currentPath.appendChild(stroke);
-
- var path = document.createElement('v:path');
- path.textpathok = 'true';
- path.v = 'm ' + x + ' ' + y + ' l ' + (x + 1) + ' ' + y;
-
- currentPath.appendChild(path);
-
- var tp = document.createElement('v:textpath');
- tp.style.cssText = 'v-text-align:' + align;
- tp.style.fontSize = Math.round(currentState.fontSize / vmlScale) + 'px';
-
- // FIXME: Font-family seems to be ignored for textpath
- tp.style.fontFamily = currentState.fontFamily;
- tp.string = str;
- tp.on = 'true';
-
- // Bold
- if ((currentState.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
- {
- tp.style.fontWeight = 'bold';
- }
-
- // Italic
- if ((currentState.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
- {
- tp.style.fontStyle = 'italic';
- }
-
- // FIXME: Text decoration not supported in textpath
- if ((currentState.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
- {
- tp.style.textDecoration = 'underline';
- }
-
- // LATER: Find vertical center for div via CSS if possible
- if (valign == 'top')
- {
- currentPath.style.top = (y + currentState.fontSize / 2) + 'px';
- }
- else if (valign == 'bottom')
- {
- currentPath.style.top = (y - currentState.fontSize / 3) + 'px';
- }
-
- currentPath.appendChild(tp);
- }
- else
- {
- currentPath = document.createElementNS(mxConstants.NS_SVG, 'text');
- currentPath.setAttribute('fill', currentState.fontColor);
- currentPath.setAttribute('font-family', currentState.fontFamily);
- currentPath.setAttribute('font-size', currentState.fontSize);
- currentPath.setAttribute('stroke', 'none');
- currentPath.setAttribute('x', x);
- currentPath.appendChild(document.createTextNode(str));
-
- // Bold
- if ((currentState.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
- {
- currentPath.setAttribute('font-weight', 'bold');
- }
-
- // Italic
- if ((currentState.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
- {
- currentPath.setAttribute('font-style', 'italic');
- }
-
- // Underline
- if ((currentState.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
- {
- currentPath.setAttribute('text-decoration', uline);
- }
-
- // Horizontal alignment
- if (align == 'left')
- {
- align = 'start';
- }
- else if (align == 'center')
- {
- align = 'middle';
- }
- else if (align == 'right')
- {
- align = 'end';
- }
-
- currentPath.setAttribute('text-anchor', align);
-
- // Vertical alignment
- // Uses dy because FF ignores alignment-baseline
- if (valign == 'top')
- {
- currentPath.setAttribute('y', y + currentState.fontSize / 5);
- currentPath.setAttribute('dy', '1ex');
- }
- else if (valign == 'middle')
- {
- currentPath.setAttribute('y', y + currentState.fontSize / 8);
- currentPath.setAttribute('dy', '0.5ex');
- }
- else
- {
- currentPath.setAttribute('y', y);
- }
-
- if (svgTransform.length > 0)
- {
- currentPath.setAttribute('transform', svgTransform);
- }
- }
-
- parentNode.appendChild(currentPath);
- }
- }
- else if (fillOp || strokeOp || fillStrokeOp)
- {
- if (currentPath != null)
- {
- var pattern = null;
-
- if (currentState.dashed)
- {
- var f = (vml) ? minScale : Number(currentPath.getAttribute('stroke-width'));
- var pat = [];
-
- for (var i = 0; i < currentState.dashpattern.length; i++)
- {
- pat.push(Math.max(1, Math.round(Number(currentState.dashpattern[i]) * f)));
- }
-
- pattern = pat.join(' ');
- }
-
- if (strokeOp || fillStrokeOp)
- {
- if (vml)
- {
- var stroke = document.createElement('v:stroke');
- stroke.endcap = currentState.linecap || 'flat';
- stroke.joinstyle = currentState.linejoin || 'miter';
- stroke.miterlimit = currentState.miterlimit || '10';
- currentPath.appendChild(stroke);
-
- // TODO: Dashpattern support in VML is limited, we should
- // map this to VML or allow for a separate VML dashstyle.
- if (pattern != null)
- {
- stroke.dashstyle = pattern;
- }
- }
- else
- {
- if (currentState.linejoin != null)
- {
- currentPath.setAttribute('stroke-linejoin', currentState.linejoin);
- }
-
- if (currentState.linecap != null)
- {
- // flat is called butt in SVG
- var value = currentState.linecap;
-
- if (value == 'flat')
- {
- value = 'butt';
- }
-
- currentPath.setAttribute('stroke-linecap', value);
- }
-
- if (currentState.miterlimit != null)
- {
- currentPath.setAttribute('stroke-miterlimit', currentState.miterlimit);
- }
-
- // Handles dash pattern
- if (pattern != null)
- {
- currentPath.setAttribute('stroke-dasharray', pattern);
- }
- }
- }
-
- // Adds the shadow
- if (background && shape.isShadow)
- {
- var dx = mxConstants.SHADOW_OFFSET_X * shape.scale;
- var dy = mxConstants.SHADOW_OFFSET_Y * shape.scale;
-
- // Adds the shadow
- if (vml)
- {
- var shadow = document.createElement('v:shadow');
- shadow.setAttribute('on', 'true');
- shadow.setAttribute('color', mxConstants.SHADOWCOLOR);
- shadow.setAttribute('offset', Math.round(dx) + 'px,' + Math.round(dy) + 'px');
- shadow.setAttribute('opacity', (mxConstants.SHADOW_OPACITY * 100) + '%');
-
- var stroke = document.createElement('v:stroke');
- stroke.endcap = currentState.linecap || 'flat';
- stroke.joinstyle = currentState.linejoin || 'miter';
- stroke.miterlimit = currentState.miterlimit || '10';
-
- if (pattern != null)
- {
- stroke.dashstyle = pattern;
- }
-
- shadow.appendChild(stroke);
- currentPath.appendChild(shadow);
- }
- else
- {
- var shadow = currentPath.cloneNode(true);
- shadow.setAttribute('stroke', mxConstants.SHADOWCOLOR);
-
- if (currentState.fill != null && (fillOp || fillStrokeOp))
- {
- shadow.setAttribute('fill', mxConstants.SHADOWCOLOR);
- }
- else
- {
- shadow.setAttribute('fill', 'none');
- }
-
- shadow.setAttribute('transform', 'translate(' + dx + ' ' + dy + ') ' +
- (shadow.getAttribute('transform') || ''));
- shadow.setAttribute('opacity', mxConstants.SHADOW_OPACITY);
- parentNode.appendChild(shadow);
- }
- }
-
- if (fillOp)
- {
- if (vml)
- {
- currentPath.stroked = 'false';
- }
- else
- {
- currentPath.setAttribute('stroke', 'none');
- }
- }
- else if (strokeOp)
- {
- if (vml)
- {
- currentPath.filled = 'false';
- }
- else
- {
- currentPath.setAttribute('fill', 'none');
- }
- }
-
- parentNode.appendChild(currentPath);
- }
-
- // Background was painted
- if (background)
- {
- background = false;
- }
- }
- else if (name == 'linecap')
- {
- currentState.linecap = node.getAttribute('cap');
- }
- else if (name == 'linejoin')
- {
- currentState.linejoin = node.getAttribute('join');
- }
- else if (name == 'miterlimit')
- {
- currentState.miterlimit = node.getAttribute('limit');
- }
- else if (name == 'dashed')
- {
- currentState.dashed = node.getAttribute('dashed') == '1';
- }
- else if (name == 'dashpattern')
- {
- var value = node.getAttribute('pattern');
-
- if (value != null && value.length > 0)
- {
- currentState.dashpattern = value.split(' ');
- }
- }
- else if (name == 'strokewidth')
- {
- currentState.strokeWidth = node.getAttribute('width') * minScale;
-
- if (vml)
- {
- currentState.strokeWidth /= vmlScale;
- }
- }
- else if (name == 'strokecolor')
- {
- currentState.stroke = node.getAttribute('color');
- }
- else if (name == 'fillcolor')
- {
- currentState.fill = node.getAttribute('color');
- currentState.fillColorAssigned = true;
- }
- else if (name == 'alpha')
- {
- currentState.alpha = Number(node.getAttribute('alpha'));
- }
- else if (name == 'fontcolor')
- {
- currentState.fontColor = node.getAttribute('color');
- }
- else if (name == 'fontsize')
- {
- currentState.fontSize = Number(node.getAttribute('size')) * minScale;
- }
- else if (name == 'fontfamily')
- {
- currentState.fontFamily = node.getAttribute('family');
- }
- else if (name == 'fontstyle')
- {
- currentState.fontStyle = Number(node.getAttribute('style'));
- }
- };
-
- // Adds a transparent rectangle in the background for hit-detection in SVG
- if (!vml)
- {
- var rect = document.createElementNS(mxConstants.NS_SVG, 'rect');
- rect.setAttribute('x', bounds.x);
- rect.setAttribute('y', bounds.y);
- rect.setAttribute('width', bounds.width);
- rect.setAttribute('height', bounds.height);
- rect.setAttribute('fill', 'none');
- rect.setAttribute('stroke', 'none');
- parentNode.appendChild(rect);
- }
-
- // Background switches to false after fill/stroke of the background
- if (this.bgNode != null)
- {
- var tmp = this.bgNode.firstChild;
-
- while (tmp != null)
- {
- if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
- {
- renderNode.call(this, tmp);
- }
-
- tmp = tmp.nextSibling;
- }
- }
- else
- {
- background = false;
- }
-
- if (this.fgNode != null)
- {
- var tmp = this.fgNode.firstChild;
-
- while (tmp != null)
- {
- if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
- {
- renderNode.call(this, tmp);
- }
-
- tmp = tmp.nextSibling;
- }
- }
- }
-};
-
-/**
- * Function: drawShape
- *
- * Draws this stencil inside the given bounds.
- */
-mxStencil.prototype.drawShape = function(canvas, state, bounds, background)
-{
- // TODO: Unify with renderDom, check performance of pluggable shape,
- // internal structure (array of special structs?), relative and absolute
- // coordinates (eg. note shape, process vs star, actor etc.), text rendering
- // and non-proportional scaling, how to implement pluggable edge shapes
- // (start, segment, end blocks), pluggable markers, how to implement
- // swimlanes (title area) with this API, add icon, horizontal/vertical
- // label, indicator for all shapes, rotation
- var node = (background) ? this.bgNode : this.fgNode;
-
- if (node != null)
- {
- var direction = mxUtils.getValue(state.style, mxConstants.STYLE_DIRECTION, null);
- var aspect = this.computeAspect(state, bounds, direction);
- var minScale = Math.min(aspect.width, aspect.height);
- var sw = (this.strokewidth == 'inherit') ?
- Number(mxUtils.getNumber(state.style, mxConstants.STYLE_STROKEWIDTH, 1)) * state.view.scale :
- Number(this.strokewidth) * minScale;
- this.lastMoveX = 0;
- this.lastMoveY = 0;
- canvas.setStrokeWidth(sw);
-
- var tmp = node.firstChild;
-
- while (tmp != null)
- {
- if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT)
- {
- this.drawNode(canvas, state, tmp, aspect);
- }
-
- tmp = tmp.nextSibling;
- }
-
- return true;
- }
-
- return false;
-};
-
-/**
- * Function: computeAspect
- *
- * Returns a rectangle that contains the offset in x and y and the horizontal
- * and vertical scale in width and height used to draw this shape inside the
- * given <mxRectangle>.
- *
- * Parameters:
- *
- * state - <mxCellState> for which the shape should be drawn.
- * bounds - <mxRectangle> that should contain the stencil.
- * direction - Optional direction of the shape to be darwn.
- */
-mxStencil.prototype.computeAspect = function(state, bounds, direction)
-{
- var x0 = bounds.x;
- var y0 = bounds.y;
- var sx = bounds.width / this.w0;
- var sy = bounds.height / this.h0;
-
- var inverse = (direction == 'north' || direction == 'south');
-
- if (inverse)
- {
- sy = bounds.width / this.h0;
- sx = bounds.height / this.w0;
-
- var delta = (bounds.width - bounds.height) / 2;
-
- x0 += delta;
- y0 -= delta;
- }
-
- if (this.aspect == 'fixed')
- {
- sy = Math.min(sx, sy);
- sx = sy;
-
- // Centers the shape inside the available space
- if (inverse)
- {
- x0 += (bounds.height - this.w0 * sx) / 2;
- y0 += (bounds.width - this.h0 * sy) / 2;
- }
- else
- {
- x0 += (bounds.width - this.w0 * sx) / 2;
- y0 += (bounds.height - this.h0 * sy) / 2;
- }
- }
-
- return new mxRectangle(x0, y0, sx, sy);
-};
-
-/**
- * Function: drawNode
- *
- * Draws this stencil inside the given bounds.
- */
-mxStencil.prototype.drawNode = function(canvas, state, node, aspect)
-{
- var name = node.nodeName;
- var x0 = aspect.x;
- var y0 = aspect.y;
- var sx = aspect.width;
- var sy = aspect.height;
- var minScale = Math.min(sx, sy);
-
- // LATER: Move to lookup table
- if (name == 'save')
- {
- canvas.save();
- }
- else if (name == 'restore')
- {
- canvas.restore();
- }
- else if (name == 'path')
- {
- canvas.begin();
-
- // Renders the elements inside the given path
- var childNode = node.firstChild;
-
- while (childNode != null)
- {
- if (childNode.nodeType == mxConstants.NODETYPE_ELEMENT)
- {
- this.drawNode(canvas, state, childNode, aspect);
- }
-
- childNode = childNode.nextSibling;
- }
- }
- else if (name == 'close')
- {
- canvas.close();
- }
- else if (name == 'move')
- {
- this.lastMoveX = x0 + Number(node.getAttribute('x')) * sx;
- this.lastMoveY = y0 + Number(node.getAttribute('y')) * sy;
- canvas.moveTo(this.lastMoveX, this.lastMoveY);
- }
- else if (name == 'line')
- {
- this.lastMoveX = x0 + Number(node.getAttribute('x')) * sx;
- this.lastMoveY = y0 + Number(node.getAttribute('y')) * sy;
- canvas.lineTo(this.lastMoveX, this.lastMoveY);
- }
- else if (name == 'quad')
- {
- this.lastMoveX = x0 + Number(node.getAttribute('x2')) * sx;
- this.lastMoveY = y0 + Number(node.getAttribute('y2')) * sy;
- canvas.quadTo(x0 + Number(node.getAttribute('x1')) * sx,
- y0 + Number(node.getAttribute('y1')) * sy,
- this.lastMoveX, this.lastMoveY);
- }
- else if (name == 'curve')
- {
- this.lastMoveX = x0 + Number(node.getAttribute('x3')) * sx;
- this.lastMoveY = y0 + Number(node.getAttribute('y3')) * sy;
- canvas.curveTo(x0 + Number(node.getAttribute('x1')) * sx,
- y0 + Number(node.getAttribute('y1')) * sy,
- x0 + Number(node.getAttribute('x2')) * sx,
- y0 + Number(node.getAttribute('y2')) * sy,
- this.lastMoveX, this.lastMoveY);
- }
- else if (name == 'arc')
- {
- // Arc from stencil is turned into curves in image output
- var r1 = Number(node.getAttribute('rx')) * sx;
- var r2 = Number(node.getAttribute('ry')) * sy;
- var angle = Number(node.getAttribute('x-axis-rotation'));
- var largeArcFlag = Number(node.getAttribute('large-arc-flag'));
- var sweepFlag = Number(node.getAttribute('sweep-flag'));
- var x = x0 + Number(node.getAttribute('x')) * sx;
- var y = y0 + Number(node.getAttribute('y')) * sy;
-
- var curves = mxUtils.arcToCurves(this.lastMoveX, this.lastMoveY, r1, r2, angle, largeArcFlag, sweepFlag, x, y);
-
- for (var i = 0; i < curves.length; i += 6)
- {
- canvas.curveTo(curves[i], curves[i + 1], curves[i + 2],
- curves[i + 3], curves[i + 4], curves[i + 5]);
-
- this.lastMoveX = curves[i + 4];
- this.lastMoveY = curves[i + 5];
- }
- }
- else if (name == 'rect')
- {
- canvas.rect(x0 + Number(node.getAttribute('x')) * sx,
- y0 + Number(node.getAttribute('y')) * sy,
- Number(node.getAttribute('w')) * sx,
- Number(node.getAttribute('h')) * sy);
- }
- else if (name == 'roundrect')
- {
- var arcsize = node.getAttribute('arcsize');
-
- if (arcsize == 0)
- {
- arcsize = mxConstants.RECTANGLE_ROUNDING_FACTOR * 100;
- }
-
- var w = Number(node.getAttribute('w')) * sx;
- var h = Number(node.getAttribute('h')) * sy;
- var factor = Number(arcsize) / 100;
- var r = Math.min(w * factor, h * factor);
-
- canvas.roundrect(x0 + Number(node.getAttribute('x')) * sx,
- y0 + Number(node.getAttribute('y')) * sy,
- w, h, r, r);
- }
- else if (name == 'ellipse')
- {
- canvas.ellipse(x0 + Number(node.getAttribute('x')) * sx,
- y0 + Number(node.getAttribute('y')) * sy,
- Number(node.getAttribute('w')) * sx,
- Number(node.getAttribute('h')) * sy);
- }
- else if (name == 'image')
- {
- var src = this.evaluateAttribute(node, 'src', state);
-
- canvas.image(x0 + Number(node.getAttribute('x')) * sx,
- y0 + Number(node.getAttribute('y')) * sy,
- Number(node.getAttribute('w')) * sx,
- Number(node.getAttribute('h')) * sy,
- src, false, node.getAttribute('flipH') == '1',
- node.getAttribute('flipV') == '1');
- }
- else if (name == 'text')
- {
- var str = this.evaluateAttribute(node, 'str', state);
-
- canvas.text(x0 + Number(node.getAttribute('x')) * sx,
- y0 + Number(node.getAttribute('y')) * sy,
- 0, 0, str, node.getAttribute('align'),
- node.getAttribute('valign'),
- node.getAttribute('vertical'));
- }
- else if (name == 'include-shape')
- {
- var stencil = mxStencilRegistry.getStencil(node.getAttribute('name'));
-
- if (stencil != null)
- {
- var x = x0 + Number(node.getAttribute('x')) * sx;
- var y = y0 + Number(node.getAttribute('y')) * sy;
- var w = Number(node.getAttribute('w')) * sx;
- var h = Number(node.getAttribute('h')) * sy;
-
- var tmp = new mxRectangle(x, y, w, h);
- stencil.drawShape(canvas, state, tmp, true);
- stencil.drawShape(canvas, state, tmp, false);
- }
- }
- else if (name == 'fillstroke')
- {
- canvas.fillAndStroke();
- }
- else if (name == 'fill')
- {
- canvas.fill();
- }
- else if (name == 'stroke')
- {
- canvas.stroke();
- }
- else if (name == 'strokewidth')
- {
- canvas.setStrokeWidth(Number(node.getAttribute('width')) * minScale);
- }
- else if (name == 'dashed')
- {
- canvas.setDashed(node.getAttribute('dashed') == '1');
- }
- else if (name == 'dashpattern')
- {
- var value = node.getAttribute('pattern');
-
- if (value != null)
- {
- var tmp = value.split(' ');
- var pat = [];
-
- for (var i = 0; i < tmp.length; i++)
- {
- if (tmp[i].length > 0)
- {
- pat.push(Number(tmp[i]) * minScale);
- }
- }
-
- value = pat.join(' ');
- canvas.setDashPattern(value);
- }
- }
- else if (name == 'strokecolor')
- {
- canvas.setStrokeColor(node.getAttribute('color'));
- }
- else if (name == 'linecap')
- {
- canvas.setLineCap(node.getAttribute('cap'));
- }
- else if (name == 'linejoin')
- {
- canvas.setLineJoin(node.getAttribute('join'));
- }
- else if (name == 'miterlimit')
- {
- canvas.setMiterLimit(Number(node.getAttribute('limit')));
- }
- else if (name == 'fillcolor')
- {
- canvas.setFillColor(node.getAttribute('color'));
- }
- else if (name == 'fontcolor')
- {
- canvas.setFontColor(node.getAttribute('color'));
- }
- else if (name == 'fontstyle')
- {
- canvas.setFontStyle(node.getAttribute('style'));
- }
- else if (name == 'fontfamily')
- {
- canvas.setFontFamily(node.getAttribute('family'));
- }
- else if (name == 'fontsize')
- {
- canvas.setFontSize(Number(node.getAttribute('size')) * minScale);
- }
-};