diff options
Diffstat (limited to 'src/js/shape/mxImageShape.js')
-rw-r--r-- | src/js/shape/mxImageShape.js | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/src/js/shape/mxImageShape.js b/src/js/shape/mxImageShape.js new file mode 100644 index 0000000..2f1eab0 --- /dev/null +++ b/src/js/shape/mxImageShape.js @@ -0,0 +1,405 @@ +/** + * $Id: mxImageShape.js,v 1.67 2012-04-22 10:16:23 gaudenz Exp $ + * Copyright (c) 2006-2010, JGraph Ltd + */ +/** + * Class: mxImageShape + * + * Extends <mxShape> to implement an image shape. This shape is registered + * under <mxConstants.SHAPE_IMAGE> in <mxCellRenderer>. + * + * Constructor: mxImageShape + * + * Constructs a new image shape. + * + * Parameters: + * + * bounds - <mxRectangle> that defines the bounds. This is stored in + * <mxShape.bounds>. + * image - String that specifies the URL of the image. This is stored in + * <image>. + * fill - String that defines the fill color. This is stored in <fill>. + * stroke - String that defines the stroke color. This is stored in <stroke>. + * strokewidth - Optional integer that defines the stroke width. Default is + * 0. This is stored in <strokewidth>. + */ +function mxImageShape(bounds, image, fill, stroke, strokewidth) +{ + this.bounds = bounds; + this.image = (image != null) ? image : ''; + this.fill = fill; + this.stroke = stroke; + this.strokewidth = (strokewidth != null) ? strokewidth : 1; + this.isShadow = false; +}; + +/** + * Extends mxShape. + */ +mxImageShape.prototype = new mxShape(); +mxImageShape.prototype.constructor = mxImageShape; + +/** + * Variable: crisp + * + * Disables crisp rendering via attributes. Image quality defines the rendering + * quality. Default is false. + */ +mxImageShape.prototype.crisp = false; + +/** + * Variable: preserveImageAspect + * + * Switch to preserve image aspect. Default is true. + */ +mxImageShape.prototype.preserveImageAspect = true; + +/** + * Function: apply + * + * Overrides <mxShape.apply> to replace the fill and stroke colors with the + * respective values from <mxConstants.STYLE_IMAGE_BACKGROUND> and + * <mxConstants.STYLE_IMAGE_BORDER>. + * + * Applies the style of the given <mxCellState> to the shape. This + * implementation assigns the following styles to local fields: + * + * - <mxConstants.STYLE_IMAGE_BACKGROUND> => fill + * - <mxConstants.STYLE_IMAGE_BORDER> => stroke + * + * Parameters: + * + * state - <mxCellState> of the corresponding cell. + */ +mxImageShape.prototype.apply = function(state) +{ + mxShape.prototype.apply.apply(this, arguments); + + this.fill = null; + this.stroke = null; + + if (this.style != null) + { + this.fill = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_BACKGROUND); + this.stroke = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_BORDER); + this.preserveImageAspect = mxUtils.getNumber(this.style, mxConstants.STYLE_IMAGE_ASPECT, 1) == 1; + this.gradient = null; + } +}; + +/** + * Function: create + * + * Override to create HTML regardless of gradient and + * rounded property. + */ +mxImageShape.prototype.create = function() +{ + var node = null; + + if (this.dialect == mxConstants.DIALECT_SVG) + { + // Workaround: To avoid control-click on images in Firefox to + // open the image in a new window, this image needs to be placed + // inside a group with a rectangle in the foreground which has a + // fill property but no visibility and absorbs all events. + // The image in turn must have all pointer-events disabled. + node = this.createSvgGroup('rect'); + this.innerNode.setAttribute('visibility', 'hidden'); + this.innerNode.setAttribute('pointer-events', 'fill'); + + this.imageNode = document.createElementNS(mxConstants.NS_SVG, 'image'); + this.imageNode.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', this.image); + this.imageNode.setAttribute('style', 'pointer-events:none'); + this.configureSvgShape(this.imageNode); + + // Removes invalid attributes on the image node + this.imageNode.removeAttribute('stroke'); + this.imageNode.removeAttribute('fill'); + node.insertBefore(this.imageNode, this.innerNode); + + // Inserts node for background and border color rendering + if ((this.fill != null && this.fill != mxConstants.NONE) || + (this.stroke != null && this.stroke != mxConstants.NONE)) + { + this.bg = document.createElementNS(mxConstants.NS_SVG, 'rect'); + node.insertBefore(this.bg, node.firstChild); + } + + // Preserves image aspect as default + if (!this.preserveImageAspect) + { + this.imageNode.setAttribute('preserveAspectRatio', 'none'); + } + } + else + { + // Uses VML image for all non-embedded images in IE to support better + // image flipping quality and avoid workarounds for event redirection + var flipH = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_FLIPH, 0) == 1; + var flipV = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_FLIPV, 0) == 1; + var img = this.image.toUpperCase(); + + // Handles non-flipped embedded images in IE6 + if (mxClient.IS_IE && !flipH && !flipV && img.substring(0, 6) == 'MHTML:') + { + // LATER: Check if outer DIV is required or if aspect can be implemented + // by adding an offset to the image loading or the background via CSS. + this.imageNode = document.createElement('DIV'); + this.imageNode.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader ' + + '(src=\'' + this.image + '\', sizingMethod=\'scale\')'; + + node = document.createElement('DIV'); + this.configureHtmlShape(node); + node.appendChild(this.imageNode); + } + // Handles all data URL images and HTML images for IE9 with no VML support (in SVG mode) + else if (!mxClient.IS_IE || img.substring(0, 5) == 'DATA:' || document.documentMode >= 9) + { + this.imageNode = document.createElement('img'); + this.imageNode.setAttribute('src', this.image); + this.imageNode.setAttribute('border', '0'); + this.imageNode.style.position = 'absolute'; + this.imageNode.style.width = '100%'; + this.imageNode.style.height = '100%'; + + node = document.createElement('DIV'); + this.configureHtmlShape(node); + node.appendChild(this.imageNode); + } + else + { + this.imageNode = document.createElement('v:image'); + this.imageNode.style.position = 'absolute'; + this.imageNode.src = this.image; + + // Needed to draw the background and border but known + // to cause problems in print preview with https + node = document.createElement('DIV'); + this.configureHtmlShape(node); + + // Workaround for cropped images in IE7/8 + node.style.overflow = 'visible'; + node.appendChild(this.imageNode); + } + } + + return node; +}; + +/** + * Function: updateAspect + * + * Updates the aspect of the image for the given image width and height. + */ +mxImageShape.prototype.updateAspect = function(w, h) +{ + var s = Math.min(this.bounds.width / w, this.bounds.height / h); + w = Math.max(0, Math.round(w * s)); + h = Math.max(0, Math.round(h * s)); + var x0 = Math.max(0, Math.round((this.bounds.width - w) / 2)); + var y0 = Math.max(0, Math.round((this.bounds.height - h) / 2)); + var st = this.imageNode.style; + + // Positions the child node relative to the parent node + if (this.imageNode.parentNode == this.node) + { + // Workaround for duplicate offset in VML in IE8 is + // to use parent padding instead of left and top + this.node.style.paddingLeft = x0 + 'px'; + this.node.style.paddingTop = y0 + 'px'; + } + else + { + st.left = (Math.round(this.bounds.x) + x0) + 'px'; + st.top = (Math.round(this.bounds.y) + y0) + 'px'; + } + + st.width = w + 'px'; + st.height = h + 'px'; +}; + +/** + * Function: scheduleUpdateAspect + * + * Schedules an asynchronous <updateAspect> using the current <image>. + */ +mxImageShape.prototype.scheduleUpdateAspect = function() +{ + var img = new Image(); + + img.onload = mxUtils.bind(this, function() + { + mxImageShape.prototype.updateAspect.call(this, img.width, img.height); + }); + + img.src = this.image; +}; + +/** + * Function: redraw + * + * Overrides <mxShape.redraw> to preserve the aspect ratio of images. + */ +mxImageShape.prototype.redraw = function() +{ + mxShape.prototype.redraw.apply(this, arguments); + + if (this.imageNode != null && this.bounds != null) + { + // Horizontal and vertical flipping + var flipH = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_FLIPH, 0) == 1; + var flipV = mxUtils.getValue(this.style, mxConstants.STYLE_IMAGE_FLIPV, 0) == 1; + + if (this.dialect == mxConstants.DIALECT_SVG) + { + var sx = 1; + var sy = 1; + var dx = 0; + var dy = 0; + + if (flipH) + { + sx = -1; + dx = -this.bounds.width - 2 * this.bounds.x; + } + + if (flipV) + { + sy = -1; + dy = -this.bounds.height - 2 * this.bounds.y; + } + + // Adds image tansformation to existing transforms + var transform = (this.imageNode.getAttribute('transform') || '') + + ' scale('+sx+' '+sy+')'+ ' translate('+dx+' '+dy+')'; + this.imageNode.setAttribute('transform', transform); + } + else + { + // Sets default size (no aspect) + if (this.imageNode.nodeName != 'DIV') + { + this.imageNode.style.width = Math.max(0, Math.round(this.bounds.width)) + 'px'; + this.imageNode.style.height = Math.max(0, Math.round(this.bounds.height)) + 'px'; + } + + // Preserves image aspect + if (this.preserveImageAspect) + { + this.scheduleUpdateAspect(); + } + + if (flipH || flipV) + { + if (mxUtils.isVml(this.imageNode)) + { + if (flipH && flipV) + { + this.imageNode.style.rotation = '180'; + } + else if (flipH) + { + this.imageNode.style.flip = 'x'; + } + else + { + this.imageNode.style.flip = 'y'; + } + } + else + { + var filter = (this.imageNode.nodeName == 'DIV') ? 'progid:DXImageTransform.Microsoft.AlphaImageLoader ' + + '(src=\'' + this.image + '\', sizingMethod=\'scale\')' : ''; + + if (flipH && flipV) + { + filter += 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)'; + } + else if (flipH) + { + filter += 'progid:DXImageTransform.Microsoft.BasicImage(mirror=1)'; + } + else + { + filter += 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)'; + } + + if (this.imageNode.style.filter != filter) + { + this.imageNode.style.filter = filter; + } + } + } + } + } +}; + +/** + * Function: configureTransparentBackground + * + * Workaround for security warning in IE if this is used in the overlay pane + * of a diagram. + */ +mxImageShape.prototype.configureTransparentBackground = function(node) +{ + // do nothing +}; + +/** + * Function: redrawSvg + * + * Updates the SVG node(s) to reflect the latest bounds and scale. + */ +mxImageShape.prototype.redrawSvg = function() +{ + this.updateSvgShape(this.innerNode); + this.updateSvgShape(this.imageNode); + + if (this.bg != null) + { + this.updateSvgShape(this.bg); + + if (this.fill != null) + { + this.bg.setAttribute('fill', this.fill); + } + else + { + this.bg.setAttribute('fill', 'none'); + } + + if (this.stroke != null) + { + this.bg.setAttribute('stroke', this.stroke); + } + else + { + this.bg.setAttribute('stroke', 'none'); + } + + this.bg.setAttribute('shape-rendering', 'crispEdges'); + } +}; + +/** + * Function: configureSvgShape + * + * Extends method to set opacity on images. + */ +mxImageShape.prototype.configureSvgShape = function(node) +{ + mxShape.prototype.configureSvgShape.apply(this, arguments); + + if (this.imageNode != null) + { + if (this.opacity != null) + { + this.imageNode.setAttribute('opacity', this.opacity / 100); + } + else + { + this.imageNode.removeAttribute('opacity'); + } + } +}; |