text: //Command: Ellipse
  
  var global = {}; //Required
  global.x1;
  global.y1;
  global.x2;
  global.y2;
  global.x3;
  global.y3;
  global.cx;
  global.cy;
  global.width;
  global.height;
  global.rot;
  global.mode;
  
  //enums
  global.mode_MAJORDIAMETER_MINORRADIUS = 0;
  global.mode_MAJORRADIUS_MINORRADIUS   = 1;
  global.mode_ELLIPSE_ROTATION          = 2;
  
  //NOTE: main() is run every time the command is started.
  //      Use it to reset variables so they are ready to go.
  function main()
  {
      initCommand();
      clearSelection();
      global.mode = global.mode_MAJORDIAMETER_MINORRADIUS;
      global.x1      = NaN;
      global.y1      = NaN;
      global.x2      = NaN;
      global.y2      = NaN;
      global.x3      = NaN;
      global.y3      = NaN;
      setPromptPrefix(qsTr("Specify first axis start point or [Center]: "));
  }
  
  //NOTE: click() is run only for left clicks.
  //      Middle clicks are used for panning.
  //      Right clicks bring up the context menu.
  function click(x, y)
  {
      if(global.mode == global.mode_MAJORDIAMETER_MINORRADIUS)
      {
          if(isNaN(global.x1))
          {
              global.x1 = x;
              global.y1 = y;
              addRubber("ELLIPSE");
              setRubberMode("ELLIPSE_LINE");
              setRubberPoint("ELLIPSE_LINE_POINT1", global.x1, global.y1);
              appendPromptHistory();
              setPromptPrefix(qsTr("Specify first axis end point: "));
          }
          else if(isNaN(global.x2))
          {
              global.x2 = x;
              global.y2 = y;
              global.cx = (global.x1 + global.x2)/2.0;
              global.cy = (global.y1 + global.y2)/2.0;
              global.width = calculateDistance(global.x1, global.y1, global.x2, global.y2);
              global.rot = calculateAngle(global.x1, global.y1, global.x2, global.y2);
              setRubberMode("ELLIPSE_MAJORDIAMETER_MINORRADIUS");
              setRubberPoint("ELLIPSE_AXIS1_POINT1", global.x1, global.y1);
              setRubberPoint("ELLIPSE_AXIS1_POINT2", global.x2, global.y2);
              setRubberPoint("ELLIPSE_CENTER", global.cx, global.cy);
              setRubberPoint("ELLIPSE_WIDTH", global.width, 0);
              setRubberPoint("ELLIPSE_ROT", global.rot, 0);
              appendPromptHistory();
              setPromptPrefix(qsTr("Specify second axis end point or [Rotation]: "));
          }
          else if(isNaN(global.x3))
          {
              global.x3 = x;
              global.y3 = y;
              global.height = perpendicularDistance(global.x3, global.y3, global.x1, global.y1, global.x2, global.y2)*2.0;
              setRubberPoint("ELLIPSE_AXIS2_POINT2", global.x3, global.y3);
              vulcanize();
              appendPromptHistory();
              endCommand();
          }
          else
          {
              error("ELLIPSE", qsTr("This should never happen."));
          }
      }
      else if(global.mode == global.mode_MAJORRADIUS_MINORRADIUS)
      {
          if(isNaN(global.x1))
          {
              global.x1 = x;
              global.y1 = y;
              global.cx = global.x1;
              global.cy = global.y1;
              addRubber("ELLIPSE");
              setRubberMode("ELLIPSE_LINE");
              setRubberPoint("ELLIPSE_LINE_POINT1", global.x1, global.y1);
              setRubberPoint("ELLIPSE_CENTER", global.cx, global.cy);
              appendPromptHistory();
              setPromptPrefix(qsTr("Specify first axis end point: "));
          }
          else if(isNaN(global.x2))
          {
              global.x2 = x;
              global.y2 = y;
              global.width = calculateDistance(global.cx, global.cy, global.x2, global.y2)*2.0;
              global.rot = calculateAngle(global.x1, global.y1, global.x2, global.y2);
              setRubberMode("ELLIPSE_MAJORRADIUS_MINORRADIUS");
              setRubberPoint("ELLIPSE_AXIS1_POINT2", global.x2, global.y2);
              setRubberPoint("ELLIPSE_WIDTH", global.width, 0);
              setRubberPoint("ELLIPSE_ROT", global.rot, 0);
              appendPromptHistory();
              setPromptPrefix(qsTr("Specify second axis end point or [Rotation]: "));
          }
          else if(isNaN(global.x3))
          {
              global.x3 = x;
              global.y3 = y;
              global.height = perpendicularDistance(global.x3, global.y3, global.cx, global.cy, global.x2, global.y2)*2.0;
              setRubberPoint("ELLIPSE_AXIS2_POINT2", global.x3, global.y3);
              vulcanize();
              appendPromptHistory();
              endCommand();
          }
          else
          {
              error("ELLIPSE", qsTr("This should never happen."));
          }
      }
      else if(global.mode == global.mode_ELLIPSE_ROTATION)
      {
          if(isNaN(global.x1))
          {
              error("ELLIPSE", qsTr("This should never happen."));
          }
          else if(isNaN(global.x2))
          {
              error("ELLIPSE", qsTr("This should never happen."));
          }
          else if(isNaN(global.x3))
          {
              var angle = calculateAngle(global.cx, global.cy, x, y);
              global.height = Math.cos(angle*Math.PI/180.0)*global.width;
              addEllipse(global.cx, global.cy, global.width, global.height, global.rot, false);
              appendPromptHistory();
              endCommand();
          }
      }
  }
  
  //NOTE: context() is run when a context menu entry is chosen.
  function context(str)
  {
      todo("ELLIPSE", "context()");
  }
  
  //NOTE: prompt() is run when Enter is pressed.
  //      appendPromptHistory is automatically called before prompt()
  //      is called so calling it is only needed for erroneous input.
  //      Any text in the command prompt is sent as an uppercase string.
  function prompt(str)
  {
      if(global.mode == global.mode_MAJORDIAMETER_MINORRADIUS)
      {
          if(isNaN(global.x1))
          {
              if(str == "C" || str == "CENTER") //TODO: Probably should add additional qsTr calls here.
              {
                  global.mode = global.mode_MAJORRADIUS_MINORRADIUS;
                  setPromptPrefix(qsTr("Specify center point: "));
              }
              else
              {
                  var strList = str.split(",");
                  if(isNaN(strList[0]) || isNaN(strList[1]))
                  {
                      alert(qsTr("Point or option keyword required."));
                      setPromptPrefix(qsTr("Specify first axis start point or [Center]: "));
                  }
                  else
                  {
                      global.x1 = Number(strList[0]);
                      global.y1 = Number(strList[1]);
                      addRubber("ELLIPSE");
                      setRubberMode("ELLIPSE_LINE");
                      setRubberPoint("ELLIPSE_LINE_POINT1", global.x1, global.y1);
                      setPromptPrefix(qsTr("Specify first axis end point: "));
                  }
              }
          }
          else if(isNaN(global.x2))
          {
              var strList = str.split(",");
              if(isNaN(strList[0]) || isNaN(strList[1]))
              {
                  alert(qsTr("Invalid point."));
                  setPromptPrefix(qsTr("Specify first axis end point: "));
              }
              else
              {
                  global.x2 = Number(strList[0]);
                  global.y2 = Number(strList[1]);
                  global.cx = (global.x1 + global.x2)/2.0;
                  global.cy = (global.y1 + global.y2)/2.0;
                  global.width = calculateDistance(global.x1, global.y1, global.x2, global.y2);
                  global.rot = calculateAngle(global.x1, global.y1, global.x2, global.y2);
                  setRubberMode("ELLIPSE_MAJORDIAMETER_MINORRADIUS");
                  setRubberPoint("ELLIPSE_AXIS1_POINT1", global.x1, global.y1);
                  setRubberPoint("ELLIPSE_AXIS1_POINT2", global.x2, global.y2);
                  setRubberPoint("ELLIPSE_CENTER", global.cx, global.cy);
                  setRubberPoint("ELLIPSE_WIDTH", global.width, 0);
                  setRubberPoint("ELLIPSE_ROT", global.rot, 0);
                  setPromptPrefix(qsTr("Specify second axis end point or [Rotation]: "));
              }
          }
          else if(isNaN(global.x3))
          {
              if(str == "R" || str == "ROTATION") //TODO: Probably should add additional qsTr calls here.
              {
                  global.mode = global.mode_ELLIPSE_ROTATION;
                  setPromptPrefix(qsTr("Specify rotation: "));
              }
              else
              {
                  var strList = str.split(",");
                  if(isNaN(strList[0]) || isNaN(strList[1]))
                  {
                      alert(qsTr("Point or option keyword required."));
                      setPromptPrefix(qsTr("Specify second axis end point or [Rotation]: "));
                  }
                  else
                  {
                      global.x3 = Number(strList[0]);
                      global.y3 = Number(strList[1]);
                      global.height = perpendicularDistance(global.x3, global.y3, global.x1, global.y1, global.x2, global.y2)*2.0;
                      setRubberPoint("ELLIPSE_AXIS2_POINT2", global.x3, global.y3);
                      vulcanize();
                      endCommand();
                  }
              }
          }
      }
      else if(global.mode == global.mode_MAJORRADIUS_MINORRADIUS)
      {
          if(isNaN(global.x1))
          {
              var strList = str.split(",");
              if(isNaN(strList[0]) || isNaN(strList[1]))
              {
                  alert(qsTr("Invalid point."));
                  setPromptPrefix(qsTr("Specify center point: "));
              }
              else
              {
                  global.x1 = Number(strList[0]);
                  global.y1 = Number(strList[1]);
                  global.cx = global.x1;
                  global.cy = global.y1;
                  addRubber("ELLIPSE");
                  setRubberMode("ELLIPSE_LINE");
                  setRubberPoint("ELLIPSE_LINE_POINT1", global.x1, global.y1);
                  setRubberPoint("ELLIPSE_CENTER", global.cx, global.cy);
                  setPromptPrefix(qsTr("Specify first axis end point: "));
              }
          }
          else if(isNaN(global.x2))
          {
              var strList = str.split(",");
              if(isNaN(strList[0]) || isNaN(strList[1]))
              {
                  alert(qsTr("Invalid point."));
                  setPromptPrefix(qsTr("Specify first axis end point: "));
              }
              else
              {
                  global.x2 = Number(strList[0]);
                  global.y2 = Number(strList[1]);
                  global.width = calculateDistance(global.x1, global.y1, global.x2, global.y2)*2.0;
                  global.rot = calculateAngle(global.x1, global.y1, global.x2, global.y2);
                  setRubberMode("ELLIPSE_MAJORRADIUS_MINORRADIUS");
                  setRubberPoint("ELLIPSE_AXIS1_POINT2", global.x2, global.y2);
                  setRubberPoint("ELLIPSE_WIDTH", global.width, 0);
                  setRubberPoint("ELLIPSE_ROT", global.rot, 0);
                  setPromptPrefix(qsTr("Specify second axis end point or [Rotation]: "));
              }
          }
          else if(isNaN(global.x3))
          {
              if(str == "R" || str == "ROTATION") //TODO: Probably should add additional qsTr calls here.
              {
                  global.mode = global.mode_ELLIPSE_ROTATION;
                  setPromptPrefix(qsTr("Specify ellipse rotation: "));
              }
              else
              {
                  var strList = str.split(",");
                  if(isNaN(strList[0]) || isNaN(strList[1]))
                  {
                      alert(qsTr("Point or option keyword required."));
                      setPromptPrefix(qsTr("Specify second axis end point or [Rotation]: "));
                  }
                  else
                  {
                      global.x3 = Number(strList[0]);
                      global.y3 = Number(strList[1]);
                      global.height = perpendicularDistance(global.x3, global.y3, global.x1, global.y1, global.x2, global.y2)*2.0;
                      setRubberPoint("ELLIPSE_AXIS2_POINT2", global.x3, global.y3);
                      vulcanize();
                      endCommand();
                  }
              }
          }
      }
      else if(global.mode == global.mode_ELLIPSE_ROTATION)
      {
          if(isNaN(global.x1))
          {
              error("ELLIPSE", qsTr("This should never happen."));
          }
          else if(isNaN(global.x2))
          {
              error("ELLIPSE", qsTr("This should never happen."));
          }
          else if(isNaN(global.x3))
          {
              if(isNaN(str))
              {
                  alert(qsTr("Invalid angle. Input a numeric angle or pick a point."));
                  setPromptPrefix(qsTr("Specify rotation: "));
              }
              else
              {
                  var angle = Number(str);
                  global.height = Math.cos(angle*Math.PI/180.0)*global.width;
                  addEllipse(global.cx, global.cy, global.width, global.height, global.rot, false);
                  endCommand();
              }
          }
      }
  }
  