/*============================================================================*/
/*                    Copyright (c) 2005 Macromedia, Inc.                     */
/*                            All rights reserved.                            */
/*============================================================================*/
/*============================================================================*/
/*                           Arrow & L-Shape tools                            */
/*============================================================================*/
/*

The default Arrow has the following:-

                       o control point 4
                       |\
  control points 0     | \
  o--------------------+  \
  |                    |   \
  |         X      cp2 o    o control point 5
  |                    |   /
  |         +----------+  /
  |         |          | /
  |         |          |/
  |         |
  |         |
  +----o----o control point 1
    control point 3

- Control point 0 (smartShape.elem.controlPoints[0])
  This control point determines the roundness of the corner.
  When this control point is dragged for the first time, its horizontal
  movement is restricted such that it does NOT cross the point "X".
  The inner corner can be rounded on the subsequent drag.
  If this control point is dragged (& mouseup) to the left of "X", however,
  the previous restriction will apply.

- Control point 1 (smartShape.elem.controlPoints[1])
  This control point determines the thickness of the shape.

- Control points 2 & 3
  (smartShape.elem.controlPoints[2] & smartShape.elem.controlPoints[3])
  These control points determine the positions of their respective ends, but
  does not affect the 'thickness' of the shape.

- Control point 4 (smartShape.elem.controlPoints[4])
  This control point determines the horizontal size of the tip.

- Control point 5 (smartShape.elem.controlPoints[5])
  This control point determines the vertical size of the tip.


The default L-shape has the following:-

  control points 0
  o--------------------+
  |                    |
  |         X          o control point 2
  |                    |
  |         +----------+
  |         |
  |         |
  |         |
  |         |
  +----o----o control point 1
    control point 3

- Control point 0 (smartShape.elem.controlPoints[0])
  This control point determines the roundness of the corner.
  When this control point is dragged for the first time, its horizontal
  movement is restricted such that it does NOT cross the point "X".
  The inner corner can be rounded on the subsequent drag.
  If this control point is dragged (& mouseup) to the left of "X", however,
  the previous restriction will apply.

- Control point 1 (smartShape.elem.controlPoints[1])
  This control point determines the the thickness of the shape.

- Control points 2 & 3
  (smartShape.elem.controlPoints[2] & smartShape.elem.controlPoints[3])
  These control points determine the positions of their respective ends, but
  does not affect the 'thickness' of the shape.

*/

/*============================================================================*/
/*                                  Tooltips                                  */
/*============================================================================*/

function initTooltips(lang, t)
{
	switch (lang)
	{
		// German
		case "de":
		case "ge":
			t.roundness = "Rundung";
			t.thickness = "Stärke";
			t.arrowsize = "Pfeilgröße";
			t.arrowtip  = "Pfeilspitze";
			t.width = "Breite";
			t.height = "Höhe";
			break;

		// French
		case "fr":
			t.roundness = "Arrondi";
			t.thickness = "Epaisseur";
			t.arrowsize = "Taille de la flèche";
			t.arrowtip  = "Pointe de la flèche";
			t.width = "Largeur";
			t.height = "Hauteur";
			break;

		// Italian
		case "it":
			t.roundness = "Smusso";
			t.thickness = "Spessore";
			t.arrowsize = "Dimensioni freccia";
			t.arrowtip  = "Punta freccia";
			t.width = "Larghezza";
			t.height = "Altezza";
			break;

		// Spanish
		case "es":
		case "sp":
			t.roundness = "Redondez";
			t.thickness = "Grosor";
			t.arrowsize = "Tamaño de flecha";
			t.arrowtip  = "Punta de la flecha";
			t.width = "Anchura";
			t.height = "Altura";
			break;

		// Japanese
		case "ja":
			t.roundness = "角丸";
			t.thickness = "太さ";
			t.arrowsize = "矢印のサイズ";
			t.arrowtip  = "矢印の先端";
			t.width = "幅";
			t.height = "高さ";
			break;

		// Chinese (Simplified)
		case "si":
			t.roundness = "圆度";
			t.thickness = "厚度";
			t.arrowsize = "箭头大小";
			t.arrowtip  = "箭头";
			t.width = "宽度";
			t.height = "高度";
			break;

		// Chinese (Traditional)
		case "tr":
			t.roundness = "圓度";
			t.thickness = "粗細";
			t.arrowsize = "箭頭大小";
			t.arrowtip  = "箭頭尖端";
			t.width = "寬度";
			t.height = "高度";
			break;

		// Korean
		case "ko":
			t.roundness = "둥글기";
			t.thickness = "두께";
			t.arrowsize = "화살표 크기";
			t.arrowtip  = "화살표 끝";
			t.width = "폭";
			t.height = "높이";
			break;

		// English
		case "en":
		default:
			t.roundness = "Roundness";
			t.thickness = "Thickness";
			t.arrowsize = "Arrow Size";
			t.arrowtip  = "Arrow Tip";
			t.width = "Width";
			t.height = "Height";
			break;
	}
	return t;
}

/*============================================================================*/
/*                             Default functions                              */
/*============================================================================*/

switch(smartShape.operation)
{
	case "BeginDragInsert":
		InsertSmartShapeAt();
		RegisterDragInsert();
		break;

	case "EndDragInsert":
		EndDragInsert();
		break;

	case "InsertSmartShapeAt":
		InsertSmartShapeAt();
		break;

	case "BeginDragControlPoint":
		BeginDragControlPoint();
		break;

	case "DragControlPoint":
		DragControlPoint();
		break;

	case "EndDragControlPoint":
		EndDragControlPoint();
		break;

	case "RedrawSmartShape":
		RedrawSmartShape();
		break;
}

function InsertSmartShapeAt()
{
	var tmp  = (Files.getLanguageDirectory()).split("/");
	var lang = ((tmp[tmp.length-1]).substr(0,2)).toLowerCase();
	var t    = initTooltips(lang, new Object());

	var isArrow = false;	// true=Arrow, false=L-Shape
	var cmp     = smartShape.currentMousePos;
	var c       = {x:cmp.x, y:cmp.y};
	var contour;

	var c0, c1, c2, c3, c4, c5;

	smartShape.elem.controlPoints.length = (isArrow)? 6 : 4;

	// roundness
	c0         = smartShape.elem.controlPoints[0];
	c0.x       = c.x;
	c0.y       = c.y + ((isArrow)? 50 : 0);
	c0.toolTip = t.roundness;

	// thickness
	c1         = smartShape.elem.controlPoints[1];
	c1.x       = c.x + 100;
	c1.y       = c.y + ((isArrow)? 150 : 200);
	c1.toolTip = t.thickness;

	// right
	c2         = smartShape.elem.controlPoints[2];
	c2.x       = c.x + ((isArrow)? 200 : 200);
	c2.y       = c.y + ((isArrow)? 100 :  50);
	c2.toolTip = t.width;

	// bottom
	c3         = smartShape.elem.controlPoints[3];
	c3.x       = c.x + 50;
	c3.y       = c.y + ((isArrow)? 150 : 200);
	c3.toolTip = t.height;

	if (isArrow)
	{
		c4         = smartShape.elem.controlPoints[4];
		c4.x       = c.x+200;
		c4.y       = c.y;
		c4.toolTip = t.arrowsize;

		c5         = smartShape.elem.controlPoints[5];
		c5.x       = c.x+300;
		c5.y       = c.y+100;
		c5.toolTip = t.arrowtip;
	}

	createShape(0, isArrow);

	smartShape.elem.customData["width"]     = 200;
	smartShape.elem.customData["height"]    = (isArrow)? 100 : 200;
	smartShape.elem.customData["thickness"] = 100;
	smartShape.elem.customData["roundness"] = 0;

	if (isArrow)
	{
		smartShape.elem.customData["shapeName"]   = "arrow";
		smartShape.elem.customData["arrowWidth"]  = 200;
		smartShape.elem.customData["arrowLength"] = 0;
		smartShape.elem.customData["arrowTip"]    = 100;
		smartShape.elem.customData["awMin"]       = 100;
		smartShape.elem.customData["atMin"]       = 0;
	}
	else
		smartShape.elem.customData["shapeName"]   = "lshape";
}

function RegisterDragInsert()
{
	var isArrow = (smartShape.elem.customData["shapeName"]=="arrow");
	var params  = smartShape.GetDefaultMoveParms();
	var contour = smartShape.elem.elements[0].contours[0];

	if (isArrow)
	{
		params.deltaXtoX = 0;
		params.deltaYtoY = 1/4;
		contour.nodes[0].RegisterInsertBBoxMove(params);
		contour.nodes[10].RegisterInsertBBoxMove(params);
		smartShape.elem.controlPoints[0].RegisterInsertBBoxMove(params);
		smartShape.elem.controlPoints[1].RegisterInsertBBoxMove(params);
		smartShape.elem.controlPoints[3].RegisterInsertBBoxMove(params);

		params.deltaXtoX = 2/3;
		params.deltaYtoY = 1/4;
		contour.nodes[1].RegisterInsertBBoxMove(params);

		params.deltaYtoY = 1/2;
		smartShape.elem.controlPoints[2].RegisterInsertBBoxMove(params);

		params.deltaXtoX = 2/3;
		params.deltaYtoY = 0;
		contour.nodes[2].RegisterInsertBBoxMove(params);
		smartShape.elem.controlPoints[4].RegisterInsertBBoxMove(params);

		params.deltaXtoX = 1;
		params.deltaYtoY = 1/2;
		contour.nodes[3].RegisterInsertBBoxMove(params);
		smartShape.elem.controlPoints[5].RegisterInsertBBoxMove(params);

		params.deltaXtoX = 2/3;
		params.deltaYtoY = 1;
		contour.nodes[4].RegisterInsertBBoxMove(params);

		params.deltaXtoX = 2/3;
		params.deltaYtoY = 3/4;
		contour.nodes[5].RegisterInsertBBoxMove(params);

		params.deltaXtoX = 0;
		contour.nodes[6].RegisterInsertBBoxMove(params);
		contour.nodes[7].RegisterInsertBBoxMove(params);
		contour.nodes[8].RegisterInsertBBoxMove(params);
		contour.nodes[9].RegisterInsertBBoxMove(params);
	}
	else
	{
		params.deltaXtoX            = 0;
		params.deltaYtoY            = 0;
		contour.nodes[0].RegisterInsertBBoxMove(params);
		contour.nodes[7].RegisterInsertBBoxMove(params);
		smartShape.elem.controlPoints[0].RegisterInsertBBoxMove(params);

		params.deltaXtoX            = 1;
		contour.nodes[1].RegisterInsertBBoxMove(params);

		params.deltaShortestSideToY = 1/6;
		smartShape.elem.controlPoints[2].RegisterInsertBBoxMove(params);

		params.deltaShortestSideToY = 1/3;
		contour.nodes[2].RegisterInsertBBoxMove(params);

		params.deltaXtoX            = 0;
		params.deltaShortestSideToX = 1/3;
		contour.nodes[3].RegisterInsertBBoxMove(params);
		contour.nodes[4].RegisterInsertBBoxMove(params);

		params.deltaYtoY            = 1;
		params.deltaShortestSideToX = 1/3;
		params.deltaShortestSideToY = 0;
		contour.nodes[5].RegisterInsertBBoxMove(params);
		smartShape.elem.controlPoints[1].RegisterInsertBBoxMove(params);

		params.deltaShortestSideToX = 1/6;
		smartShape.elem.controlPoints[3].RegisterInsertBBoxMove(params);

		params.deltaShortestSideToX = 0;
		contour.nodes[6].RegisterInsertBBoxMove(params);
	}
}

function EndDragInsert()
{
	var isArrow = (smartShape.elem.customData["shapeName"]=="arrow");
	var c       = smartShape.elem.elements[0].contours[0];

	if (isArrow)
	{
		var c0 = smartShape.elem.controlPoints[0];
		var c1 = smartShape.elem.controlPoints[1];
		var c2 = smartShape.elem.controlPoints[2];
		var c3 = smartShape.elem.controlPoints[3];

		var w, h, h_, t, dx, dy;

		w  = c.nodes[1].x - c.nodes[0].x;
		h  = c.nodes[9].y - c.nodes[0].y;
		h_ = h/2;

		if (h>w)
		{
			h  = w;
			h_ = h/2;
			t  = w;

			moveNode_(c.nodes[ 0], c.nodes[ 0].x, c2.y-h_);
			moveNode_(c.nodes[ 1], c.nodes[ 1].x, c2.y-h_);
			moveNode_(c.nodes[ 5], c.nodes[ 5].x, c2.y+h_);
			moveNode_(c.nodes[ 6], c.nodes[ 6].x, c2.y+h_);
			moveNode_(c.nodes[ 7], c.nodes[ 7].x, c2.y+h_);
			moveNode_(c.nodes[ 8], c.nodes[ 8].x, c2.y+h_);
			moveNode_(c.nodes[ 9], c.nodes[ 9].x, c2.y+h_);
			moveNode_(c.nodes[10], c.nodes[10].x, c2.y-h_);

			c0.y = c.nodes[0].y;
		}
		else
			t = h;

		moveNode_(c.nodes[6], c.nodes[0].x+h, c.nodes[6].y);
		moveNode_(c.nodes[7], c.nodes[0].x+h, c.nodes[7].y);
		moveNode_(c.nodes[8], c.nodes[0].x+h, c.nodes[8].y);

		c1.x = c.nodes[8].x;
		c1.y = c.nodes[8].y;
		c3.x = c.nodes[8].x - h_;
		c3.y = c.nodes[8].y;

		if (c.nodes[1].x>=c.nodes[2].x)
		{
			dx = (c2.y-c.nodes[2].y)*(c.nodes[1].x-c.nodes[2].x)/(c.nodes[1].y-c.nodes[2].y);
			dx = dx - (c.nodes[1].x-c.nodes[2].x);
			dy = 2*(c.nodes[1].x-c.nodes[2].x)*(c.nodes[3].y-c.nodes[2].y)/(c.nodes[3].x-c.nodes[2].x);
		}
		else
		{
			dx = c.nodes[3].x - c.nodes[2].x;
			dy = c.nodes[5].y - c.nodes[1].y;
		}

		smartShape.elem.customData["width"]       = w;
		smartShape.elem.customData["height"]      = h;
		smartShape.elem.customData["thickness"]   = t;
		smartShape.elem.customData["roundness"]   = c.nodes[0].x - c.nodes[10].x;
		smartShape.elem.customData["arrowWidth"]  = c.nodes[4].y - c.nodes[2].y;
		smartShape.elem.customData["arrowLength"] = c.nodes[1].x - c.nodes[2].x;
		smartShape.elem.customData["arrowTip"]    = c.nodes[3].x - c.nodes[1].x;
		smartShape.elem.customData["awMin"]       = Math.round(dy);
		smartShape.elem.customData["atMin"]       = Math.round(dx);
	}
	else
	{
		smartShape.elem.customData["width"]     = c.nodes[1].x - c.nodes[7].x;
		smartShape.elem.customData["height"]    = c.nodes[6].y - c.nodes[0].y;
		smartShape.elem.customData["thickness"] = c.nodes[5].x - c.nodes[6].x;
		smartShape.elem.customData["roundness"] = c.nodes[0].x - c.nodes[7].x;
	}
}

function BeginDragControlPoint()
{
	var isArrow = (smartShape.elem.customData["shapeName"]=="arrow");
	var cpIdx   = smartShape.currentControlPointIndex;
	var c0      = smartShape.elem.controlPoints[0];
	var c1      = smartShape.elem.controlPoints[1];
	var c2      = smartShape.elem.controlPoints[2];
	var c3      = smartShape.elem.controlPoints[3];
	if (isArrow)
	{
		var c4 = smartShape.elem.controlPoints[4];
		var c5 = smartShape.elem.controlPoints[5];
	}
	var contour = smartShape.elem.elements[0].contours[0];
	var a       = (isArrow)? 3 : 0;
	var params;
	var g;
	var maxY;

	var c  = {x:contour.nodes[3+a].x, y:contour.nodes[4+a].y};
	var h  = contour.nodes[2+a].y - contour.nodes[1  ].y;
	var h_ = contour.nodes[5+a].y - contour.nodes[2+a].y;
	var w  = contour.nodes[5+a].x - contour.nodes[6+a].x;
	var w_ = contour.nodes[2+a].x - contour.nodes[5+a].x; 
	var R  = contour.nodes[0  ].x - contour.nodes[7+a].x;
	var r  = contour.nodes[3+a].x - contour.nodes[4+a].x;

	var dx, dy, lim1, lim1_, lim2, lim2_;

	switch (cpIdx)
	{
		case 0:
			// calculate how far c0 can be dragged to create rounded corner
			if (c0.x < contour.nodes[5+a].x)
			{
				dx    = w;
				dy    = h;
				lim1  = (dx>dy)? dx : dy;
				lim1_ = lim1 - (lim1 * fw.ellipseBCPConst);
				lim2  = 0;
				lim2_ = lim2 - (lim2 * fw.ellipseBCPConst);
			}
			else
			{
				dx    = Math.abs(contour.nodes[1].x - contour.nodes[6+a].x);
				dy    = Math.abs(contour.nodes[1].y - contour.nodes[6+a].y);
				lim1  = (dx<dy)? dx : dy;
				lim1_ = lim1 - (lim1 * fw.ellipseBCPConst);
				lim2  = (w<h)? lim1-h : lim1-w;
				lim2_ = lim2 - (lim2 * fw.ellipseBCPConst);
			}

			params           = smartShape.GetDefaultMoveParms();
			params.deltaYtoY = 0;
			params.minX      = contour.nodes[7+a].x;
			params.maxX      = params.minX + lim1;
			c0.RegisterMove(params);

			params.movePred  = false;
			params.movePt    = true;
			params.moveSucc  = true;
			contour.nodes[0].RegisterMove(params);

			params.movePred  = !params.movePred;
			params.movePt    = !params.movePt;
			params.moveSucc  = !params.moveSucc;
			params.deltaXtoX = lim1_/lim1;
			params.maxX      = contour.nodes[7+a].x + lim1_;
			contour.nodes[0].RegisterMove(params);

			params           = smartShape.GetDefaultMoveParms();
			params.movePred  = true;
			params.movePt    = true;
			params.moveSucc  = false;
			params.deltaYtoY = 0;
			params.minX      = contour.nodes[5+a].x;
			params.maxX      = params.minX + lim2;
			contour.nodes[3+a].RegisterMove(params);

			params.movePred  = !params.movePred;
			params.movePt    = !params.movePt;
			params.moveSucc  = !params.moveSucc;
			params.deltaXtoX = lim2_/lim2;
			params.maxX      = contour.nodes[5+a].x + lim2_;
			contour.nodes[3+a].RegisterMove(params);

			params           = smartShape.GetDefaultMoveParms();
			params.movePred  = false;
			params.movePt    = true;
			params.moveSucc  = true;
			params.deltaXtoX = 0;
			params.deltaYtoY = 0;
			params.deltaXtoY = 1;
			params.minY      = contour.nodes[2+a].y;
			params.maxY      = params.minY + lim2;
			contour.nodes[4+a].RegisterMove(params);

			params.movePred  = !params.movePred;
			params.movePt    = !params.movePt;
			params.moveSucc  = !params.moveSucc;
			params.deltaXtoY = lim2_/lim2;
			params.maxY      = contour.nodes[2+a].y + lim2_;
			contour.nodes[4+a].RegisterMove(params);

			params           = smartShape.GetDefaultMoveParms();
			params.movePred  = true;
			params.movePt    = true;
			params.moveSucc  = false;
			params.deltaXtoX = 0;
			params.deltaYtoY = 0;
			params.deltaXtoY = 1;
			params.minY      = contour.nodes[0].y;
			params.maxY      = params.minY + lim1;
			contour.nodes[7+a].RegisterMove(params);

			params.movePred  = !params.movePred;
			params.movePt    = !params.movePt;
			params.moveSucc  = !params.moveSucc;
			params.deltaXtoY = lim1_/lim1;
			params.maxY      = contour.nodes[0].y + lim1_;
			contour.nodes[7+a].RegisterMove(params);
			break;

		case 1:
			createShape(1, isArrow);

			contour = smartShape.elem.elements[1].contours[0];

			if (isArrow)
			{
				if (c4.x < c2.x)
				{
					g = (c5.y-c4.y)/(c5.x-c4.x);
					lim1 = (c0.y-c4.y) - ((c2.x-c4.x)*g);
				}
				else
					lim1 = contour.nodes[1].y - contour.nodes[2].y;
			}
			else
				lim1 = ((w_<h_)? w_ : h_) - r;

			dy = contour.nodes[5+a].y - contour.nodes[4+a].y - r;

			lim1 = (lim1<dy)? lim1 : dy;

			params           = smartShape.GetDefaultMoveParms();
			params.deltaXtoX = -1;
			params.deltaYtoY = 0;
			params.deltaXtoY = -1;
			params.minX      = contour.nodes[6+a].x - lim1;
			params.maxX      = c3.x;
			params.minY      = contour.nodes[1].y - lim1;
			params.maxY      = c2.y;
			contour.nodes[0  ].RegisterMove(params);
			contour.nodes[7+a].RegisterMove(params);

			params           = smartShape.GetDefaultMoveParms();
			params.deltaXtoX = 0;
			params.deltaYtoY = 0;
			params.deltaXtoY = -1;
			params.minY      = contour.nodes[1].y - lim1;
			params.maxY      = c2.y;
			contour.nodes[1].RegisterMove(params);

			params.deltaXtoY*= -1;
			params.minY      = c2.y;
			params.maxY      = contour.nodes[2+a].y + lim1;
			contour.nodes[2+a].RegisterMove(params);

			params           = smartShape.GetDefaultMoveParms();
			params.deltaYtoY = 0;
			params.deltaXtoY = 1;
			params.minX      = c3.x;
			params.maxX      = contour.nodes[5+a].x + lim1;
			params.minY      = c2.y;
			params.maxY      = contour.nodes[2+a].y + lim1;
			contour.nodes[3+a].RegisterMove(params);
			contour.nodes[4+a].RegisterMove(params);

			params           = smartShape.GetDefaultMoveParms();
			params.deltaYtoY = 0;
			params.minX      = c3.x;
			params.maxX      = contour.nodes[5+a].x + lim1;
			contour.nodes[5+a].RegisterMove(params);
			c1.RegisterMove(params);

			params.deltaXtoX*= -1;
			params.minX      = contour.nodes[6+a].x - lim1;
			params.maxX      = c3.x;
			contour.nodes[6+a].RegisterMove(params);

			break;

		case 2:
		case 5:
			if ( (cpIdx==5) || ((cpIdx==2) && (isArrow) && (c2.x==c5.x)) )
			{
				params           = smartShape.GetDefaultMoveParms();
				params.deltaYtoY = 0;
				params.minX      = (c4.x>=c2.x)? c4.x : (c2.x + (c2.y-c0.y)*(c2.x-c4.x)/(c0.y-c4.y));
				c5.RegisterMove(params);
				contour.nodes[3].RegisterMove(params);
			}
			else
			{
				params           = smartShape.GetDefaultMoveParms();
				params.minX      = contour.nodes[5+a].x + r + ((isArrow)? ((c4.x<c2.x)? (c2.x-c4.x) : 0) : 0);
				params.maxY      = contour.nodes[5+a].y - r;
				contour.nodes[2+a].RegisterMove(params);

				params.maxY     -= h/2;
				c2.RegisterMove(params);

				params.maxY     -= h/2;
				contour.nodes[1].RegisterMove(params);

				if (isArrow)
				{
					maxY = params.maxY;

					params.minX      = contour.nodes[5+a].x + r + ((isArrow)? ((c4.x>c2.x)? (c4.x-c2.x) : 0) : 0);
					params.maxY     -= (c5.y-c4.y-h/2);
					contour.nodes[2].RegisterMove(params);
					c4.RegisterMove(params);

					params.maxY     += (c5.y-c4.y)*2;
					contour.nodes[4].RegisterMove(params);

					params.minX     += ((c2.x-c4.x) + (c5.x-c2.x));
					params.maxY     -= (c5.y-c4.y);
					contour.nodes[3].RegisterMove(params);
					c5.RegisterMove(params);

					params.maxY = maxY;
				}

				params.deltaXtoX = 0;
				params.minX      = params.minY;
				c0.RegisterMove(params);
				contour.nodes[0].RegisterMove(params);

				params.maxY     += h;
				contour.nodes[3+a].RegisterMove(params);

				params.movePred  = false;
				params.movePt    = true;
				params.moveSucc  = true;
				params.minX      = contour.nodes[5+a].x;
				params.maxY      = contour.nodes[5+a].y;
				contour.nodes[4+a].RegisterMove(params);

				params.movePred  = !params.movePred;
				params.movePt    = !params.movePt;
				params.moveSucc  = !params.moveSucc;
				params.maxY     -= r*fw.ellipseBCPConst;
				contour.nodes[4+a].RegisterMove(params);

				params.movePred  = true;
				params.movePt    = true;
				params.moveSucc  = false;
				params.minX      = contour.nodes[7+a].x;
				params.maxY      = contour.nodes[7+a].y + (contour.nodes[5+a].y-contour.nodes[4+a].y);
				contour.nodes[7+a].RegisterMove(params);

				params.movePred  = !params.movePred;
				params.movePt    = !params.movePt;
				params.moveSucc  = !params.moveSucc;
				params.maxY     -= R*fw.ellipseBCPConst;
				contour.nodes[7+a].RegisterMove(params);
			}
			break;

		case 3:
			params           = smartShape.GetDefaultMoveParms();
			params.maxX      = contour.nodes[2+a].x - r;
			params.minY      = contour.nodes[2+a].y + r;
			contour.nodes[5+a].RegisterMove(params);
			c1.RegisterMove(params);
			params.maxX     -= w/2;
			c3.RegisterMove(params);
			params.maxX     -= w/2;
			contour.nodes[6+a].RegisterMove(params);

			params.deltaYtoY = 0;
			params.minY      = params.minX;
			contour.nodes[7+a].RegisterMove(params);

			params.maxX     += w;
			contour.nodes[4+a].RegisterMove(params);

			params.movePred  = true;
			params.movePt    = true;
			params.moveSucc  = false;
			params.maxX      = contour.nodes[2+a].x;
			contour.nodes[3+a].RegisterMove(params);

			params.movePred  = !params.movePred;
			params.movePt    = !params.movePt;
			params.moveSucc  = !params.moveSucc;
			params.maxX     -= r*fw.ellipseBCPConst;
			contour.nodes[3+a].RegisterMove(params);

			params.movePred  = false;
			params.movePt    = true;
			params.moveSucc  = true;
			params.maxX      = contour.nodes[0].x + (contour.nodes[2+a].x-contour.nodes[3+a].x);
			contour.nodes[0].RegisterMove(params);
			c0.RegisterMove(params);

			params.movePred  = !params.movePred;
			params.movePt    = !params.movePt;
			params.moveSucc  = !params.moveSucc;
			params.maxX     -= R*fw.ellipseBCPConst;
			contour.nodes[0].RegisterMove(params);

			break;
	}
}

function DragControlPoint()
{
	if ((smartShape.currentControlPointIndex==4) && 
		(smartShape.elem.customData["shapeName"]=="arrow"))
	{
		var c0 = smartShape.elem.controlPoints[0];
		var c1 = smartShape.elem.controlPoints[1];
		var c2 = smartShape.elem.controlPoints[2];
		var c4 = smartShape.elem.controlPoints[4];
		var c5 = smartShape.elem.controlPoints[5];
		var c  = smartShape.elem.elements[0].contours[0];
		var cm = smartShape.currentMousePos;
		var mg = (c2.y-c0.y)/(c5.x-c2.x);
		var g, h;

		g = (c2.y - cm.y)/(c5.x - cm.x);

		if      (cm.x >= c2.x) c4.x = c2.x;
		else if (cm.x <  c1.x) c4.x = c1.x;
		else if (cm.x <  c1.x) c4.x = c1.x;
		else                   c4.x = cm.x;

		if      (g < mg)       c4.y = c2.y - (mg * (c5.x-c4.x))
		else                   c4.y = cm.y;

		h = (c2.y - c4.y)*2;

		moveNode(c.nodes[2], c4.x, c4.y,   c4.x, c4.y,   c4.x, c4.y  );
		moveNode(c.nodes[4], c4.x, c4.y+h, c4.x, c4.y+h, c4.x, c4.y+h);
	}
}

function EndDragControlPoint()
{
	var isArrow = (smartShape.elem.customData["shapeName"]=="arrow");
	var c2      = smartShape.elem.controlPoints[2];
	var c       = smartShape.elem.elements[0].contours[0];
	var a       = (isArrow)? 3 : 0;

	if (smartShape.currentControlPointIndex==1)
		recreateShape(isArrow);

	smartShape.elem.customData["width"]     = c.nodes[1  ].x - c.nodes[7+a].x;
	smartShape.elem.customData["height"]    = c.nodes[6+a].y - c.nodes[0  ].y;
	smartShape.elem.customData["thickness"] = c.nodes[5+a].x - c.nodes[6+a].x;
	smartShape.elem.customData["roundness"] = c.nodes[0  ].x - c.nodes[7+a].x;

	if (isArrow)
	{
		smartShape.elem.customData["arrowWidth"]  = c.nodes[4].y - c.nodes[2].y;
		smartShape.elem.customData["arrowLength"] = c.nodes[1].x - c.nodes[2].x;
		smartShape.elem.customData["arrowTip"]    = c.nodes[3].x - c.nodes[1].x;

		var dx, dy;

		if (c.nodes[1].x>=c.nodes[2].x)
		{
			dx = (c2.y-c.nodes[2].y)*(c.nodes[1].x-c.nodes[2].x)/(c.nodes[1].y-c.nodes[2].y);
			dx = dx - (c.nodes[1].x-c.nodes[2].x);
			dy = 2*(c.nodes[1].x-c.nodes[2].x)*(c.nodes[3].y-c.nodes[2].y)/(c.nodes[3].x-c.nodes[2].x);
		}
		else
		{
			dx = c.nodes[3].x - c.nodes[2].x;
			dy = c.nodes[5].y - c.nodes[1].y;
		}

		smartShape.elem.customData["awMin"] = Math.round(dy);
		smartShape.elem.customData["atMin"] = Math.round(dx);
	}
}

function RedrawSmartShape()
{
	var isArrow = (smartShape.elem.customData["shapeName"]=="arrow");
	var c0      = smartShape.elem.controlPoints[0];
	var c1      = smartShape.elem.controlPoints[1];
	var c2      = smartShape.elem.controlPoints[2];
	var c3      = smartShape.elem.controlPoints[3];
	if (isArrow)
	{
		var c4 = smartShape.elem.controlPoints[4];
		var c5 = smartShape.elem.controlPoints[5];
	}

	var w  = parseInt(smartShape.elem.customData["width"]);
	var h  = parseInt(smartShape.elem.customData["height"]);
	var t  = parseInt(smartShape.elem.customData["thickness"]);
	var r  = parseInt(smartShape.elem.customData["roundness"]);
	if (isArrow)
	{
		var aw = parseInt(smartShape.elem.customData["arrowWidth"]);
		var al = parseInt(smartShape.elem.customData["arrowLength"]);
		var at = parseInt(smartShape.elem.customData["arrowTip"]);
	}
	var r_ = (r>t)? r-t : 0;
	var c  = smartShape.elem.elements[0].contours[0];
	var a  = (isArrow)? 3 : 0;
	var x  = c.nodes[7+a].x;
	var y  = c.nodes[0  ].y;

	moveNode(c.nodes[0], x+r, y, x+r-r*fw.ellipseBCPConst, y, x+r, y);
	if (isArrow)
	{
		moveNode_(c.nodes[1], x+w,    y         );
		moveNode_(c.nodes[2], x+w-al, y+(t-aw)/2);
		moveNode_(c.nodes[3], x+w+at, y+(t/2)   );
		moveNode_(c.nodes[4], x+w-al, y+(t+aw)/2);
		moveNode_(c.nodes[5], x+w,    y+t       );
	}
	else
	{
		moveNode_(c.nodes[1], x+w, y  );
		moveNode_(c.nodes[2], x+w, y+t);
	}
	moveNode (c.nodes[3+a], x+t+r_, y+t,    x+t+r_, y+t,               x+t+r_*fw.ellipseBCPConst, y+t          );
	moveNode (c.nodes[4+a], x+t,    y+t+r_, x+t,    y+t+r_-r_*fw.ellipseBCPConst, x+t,            y+t+r_       );
	moveNode_(c.nodes[5+a], x+t,    y+h);
	moveNode_(c.nodes[6+a], x,      y+h);
	moveNode (c.nodes[7+a], x,      y+r,    x,      y+r,               x,              y+r-r*fw.ellipseBCPConst);

	c0.x = x + r;
	c0.y = y;

	c1.x = x + t;
	c1.y = y + h;

	c2.x = c.nodes[1].x;
	c2.y = y + t/2;

	c3.x = x + t/2;
	c3.y = y + h;

	if (isArrow)
	{
		c4.x = c.nodes[2].x;
		c4.y = c.nodes[2].y;

		c5.x = c.nodes[3].x;
		c5.y = c.nodes[3].y;

		var dx, dy;

		if (c.nodes[1].x>=c.nodes[2].x)
		{
			dx = (c2.y-c.nodes[2].y)*(c.nodes[1].x-c.nodes[2].x)/(c.nodes[1].y-c.nodes[2].y);
			dx = dx - (c.nodes[1].x-c.nodes[2].x);
			dy = 2*(c.nodes[1].x-c.nodes[2].x)*(c.nodes[3].y-c.nodes[2].y)/(c.nodes[3].x-c.nodes[2].x);
		}
		else
		{
			dx = c.nodes[3].x - c.nodes[2].x;
			dy = c.nodes[5].y - c.nodes[1].y;
		}

		smartShape.elem.customData["awMin"] = Math.round(dy);
		smartShape.elem.customData["atMin"] = Math.round(dx);
	}
}

/*============================================================================*/
/*                           User defined functions                           */
/*============================================================================*/

// returns distance between (x1,y1) and (x2,y2)

function dist(x1, y1, x2, y2)
{
	return(Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)));
}

// adds a point with no handles at (x,y)
function addPathPoint(contour, x, y)
{
	addPathPointBez(contour, x, y, x, y, x, y);
}

// adds a point at (x,y) with specified handle positions
function addPathPointBez(contour, x, y, predX, predY, succX, succY)
{
	var theNodes = contour.nodes;

	// increase the length to add a new point
	theNodes.length++;

	// get the new point
	var node = theNodes[theNodes.length - 1];
	
	// Set the new point's values
	node.x     = x;
	node.y     = y;
	node.predX = predX;
	node.predY = predY;
	node.succX = succX;
	node.succY = succY;
}

function createShape(e, isArrow)
{
	var cpIdx = smartShape.currentControlPointIndex;
	var c0    = smartShape.elem.controlPoints[0];
	var c1    = smartShape.elem.controlPoints[1];
	var c2    = smartShape.elem.controlPoints[2];
	var c3    = smartShape.elem.controlPoints[3];
	var w2    = c1.x - c3.x;

	if (isArrow)
	{
		var c4 = smartShape.elem.controlPoints[4];
		var c5 = smartShape.elem.controlPoints[5];
	}

	// allocate space for the shape
	smartShape.elem.elements[e] = new Path;
	smartShape.elem.elements[e].contours[0] = new Contour;

	var contour = smartShape.elem.elements[e].contours[0];

	contour.nodes.length = 0;

	addPathPoint(contour, c3.x-w2, c2.y-w2);
	addPathPoint(contour, c2.x,    c2.y-w2);

	if (isArrow)
	{
		addPathPoint(contour, c4.x, c4.y);
		addPathPoint(contour, c5.x, c5.y);
		addPathPoint(contour, c4.x, c2.y+(c2.y-c4.y));
	}

	addPathPoint(contour, c2.x,    c2.y+w2);
	addPathPoint(contour, c1.x,    c2.y+w2);
	addPathPoint(contour, c1.x,    c2.y+w2);
	addPathPoint(contour, c1.x,    c1.y   );
	addPathPoint(contour, c3.x-w2, c1.y   );
	addPathPoint(contour, c3.x-w2, c2.y-w2);

	contour.isClosed = true;
}

function recreateShape(isArrow)
{
	var c0 = smartShape.elem.controlPoints[0];
	var c1 = smartShape.elem.controlPoints[1];
	var c2 = smartShape.elem.controlPoints[2];
	var c3 = smartShape.elem.controlPoints[3];
	var e0 = smartShape.elem.elements[0].contours[0];
	var e1 = smartShape.elem.elements[1].contours[0];
	var a  = (isArrow)? 3 : 0;
	var R0 = e0.nodes[0  ].x - e0.nodes[7+a].x;
	var r0 = e0.nodes[3+a].x - e0.nodes[4+a].x;
	var s  = (e1.nodes[5+a].x-e1.nodes[6+a].x)/(e0.nodes[5+a].x-e0.nodes[6+a].x);
	var R1;
	var r1 = r0;
	var ctype;

	if (e0.nodes[0].x == e0.nodes[7+a].x)
		ctype = 0;
	else if (e0.nodes[0].x <= e0.nodes[5+a].x)
		ctype = 1;
	else
		ctype = 2;

	copyNodePos(e1.nodes[1  ], e0.nodes[1  ]);
	copyNodePos(e1.nodes[2+a], e0.nodes[2+a]);

	e0.nodes[3+a].x     = e1.nodes[5+a].x + r1;
	e0.nodes[3+a].y     = e1.nodes[2+a].y;
	e0.nodes[3+a].predX = e1.nodes[5+a].x + r1;
	e0.nodes[3+a].predY = e0.nodes[3+a].y;
	e0.nodes[3+a].succX = e0.nodes[3+a].x - (r1 * fw.ellipseBCPConst);
	e0.nodes[3+a].succY = e0.nodes[3+a].y;

	e0.nodes[4+a].x     = e1.nodes[5+a].x;
	e0.nodes[4+a].y     = e1.nodes[2+a].y + r1;
	e0.nodes[4+a].predX = e0.nodes[4+a].x;
	e0.nodes[4+a].predY = e0.nodes[4+a].y - (r1 * fw.ellipseBCPConst);
	e0.nodes[4+a].succX = e0.nodes[4+a].x;
	e0.nodes[4+a].succY = e0.nodes[4+a].y;

	copyNodePos(e1.nodes[5+a], e0.nodes[5+a]);
	copyNodePos(e1.nodes[6+a], e0.nodes[6+a]);

	R1 = (ctype==0)? 0 : ((ctype==1)? (R0 * s) : e0.nodes[3+a].x - e0.nodes[6+a].x);// * s;

	e0.nodes[0].x     = (ctype==2)? e0.nodes[3+a].x : (e0.nodes[6+a].x + ((ctype==0)? 0 : R1));
	e0.nodes[0].y     = e0.nodes[1].y;
	e0.nodes[0].predX = e0.nodes[0].x - (R1 * fw.ellipseBCPConst);
	e0.nodes[0].predY = e0.nodes[0].y;
	e0.nodes[0].succX = e0.nodes[0].x;
	e0.nodes[0].succY = e0.nodes[0].y;

	c0.x = e0.nodes[0].x;
	c0.y = e0.nodes[0].y;

	e0.nodes[7+a].x     = e0.nodes[6+a].x;
	e0.nodes[7+a].y     = (ctype==2)? e0.nodes[4+a].y : (e0.nodes[1].y + ((ctype==0)? 0 : R1));
	e0.nodes[7+a].predX = e0.nodes[7+a].x;
	e0.nodes[7+a].predY = e0.nodes[7+a].y;
	e0.nodes[7+a].succX = e0.nodes[7+a].x;
	e0.nodes[7+a].succY = e0.nodes[7+a].y - (R1 * fw.ellipseBCPConst);

	smartShape.elem.elements.length--;
}

function copyNodePos(ns, nd)
{
	nd.x     = ns.x;
	nd.y     = ns.y;
	nd.predX = ns.predX;
	nd.predY = ns.predY;
	nd.succX = ns.succX;
	nd.succY = ns.succY;
}

function moveNode(n, x, y, px, py, sx, sy)
{
	n.x     = x;
	n.y     = y;
	n.predX = px;
	n.predY = py;
	n.succX = sx;
	n.succY = sy;
}

function moveNode_(n, x, y)
{
	moveNode(n, x, y, x, y, x, y);
}

/*============================================================================*/
/*                    Copyright (c) 2005 Macromedia, Inc.                     */
/*                            All rights reserved.                            */
/*============================================================================*/