diff options
author | adhitya | 2016-04-12 07:02:39 +0000 |
---|---|---|
committer | adhitya | 2016-04-12 07:02:39 +0000 |
commit | dd83478e3fcaac98de690aa59e6288ad41a1c351 (patch) | |
tree | 38653bdf0ae95053f66777c4ac3fe5be5d8fbd33 /src/js/shape/mxStencil.js | |
parent | 92f3207b50a1caca07df5c5b238212af3358905b (diff) | |
download | xcos-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.js | 1585 |
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); - } -}; |