summaryrefslogtreecommitdiff
path: root/src/js/shape/mxImageShape.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/shape/mxImageShape.js')
-rw-r--r--src/js/shape/mxImageShape.js405
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');
+ }
+ }
+};