text: /**
   * $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');
  		}
  	}
  };
  